11using System ;
2+ using System . Collections . Generic ;
3+ using System . Linq ;
24using Coder . Desktop . App . Converters ;
35using Coder . Desktop . MutagenSdk . Proto . Synchronization ;
6+ using Coder . Desktop . MutagenSdk . Proto . Synchronization . Core ;
47using Coder . Desktop . MutagenSdk . Proto . Url ;
58
69namespace Coder . Desktop . App . Models ;
@@ -48,7 +51,7 @@ public string Description(string linePrefix = "")
4851public class SyncSessionModel
4952{
5053 public readonly string Identifier ;
51- public readonly string Name ;
54+ public readonly DateTime CreatedAt ;
5255
5356 public readonly string AlphaName ;
5457 public readonly string AlphaPath ;
@@ -62,14 +65,24 @@ public class SyncSessionModel
6265 public readonly SyncSessionModelEndpointSize AlphaSize ;
6366 public readonly SyncSessionModelEndpointSize BetaSize ;
6467
65- public readonly string [ ] Errors = [ ] ;
68+ public readonly IReadOnlyList < string > Conflicts ; // Conflict descriptions
69+ public readonly ulong OmittedConflicts ;
70+ public readonly IReadOnlyList < string > Errors ;
71+
72+ // If Paused is true, the session can be resumed. If false, the session can
73+ // be paused.
74+ public bool Paused => StatusCategory is SyncSessionStatusCategory . Paused or SyncSessionStatusCategory . Halted ;
6675
6776 public string StatusDetails
6877 {
6978 get
7079 {
71- var str = $ "{ StatusString } ({ StatusCategory } )\n \n { StatusDescription } ";
72- foreach ( var err in Errors ) str += $ "\n \n { err } ";
80+ var str = StatusString ;
81+ if ( StatusCategory . ToString ( ) != StatusString ) str += $ " ({ StatusCategory } )";
82+ str += $ "\n \n { StatusDescription } ";
83+ foreach ( var err in Errors ) str += $ "\n \n -----\n \n { err } ";
84+ foreach ( var conflict in Conflicts ) str += $ "\n \n -----\n \n { conflict } ";
85+ if ( OmittedConflicts > 0 ) str += $ "\n \n -----\n \n { OmittedConflicts : N0} conflicts omitted";
7386 return str ;
7487 }
7588 }
@@ -84,41 +97,10 @@ public string SizeDetails
8497 }
8598 }
8699
87- // TODO: remove once we process sessions from the mutagen RPC
88- public SyncSessionModel ( string alphaPath , string betaName , string betaPath ,
89- SyncSessionStatusCategory statusCategory ,
90- string statusString , string statusDescription , string [ ] errors )
91- {
92- Identifier = "TODO" ;
93- Name = "TODO" ;
94-
95- AlphaName = "Local" ;
96- AlphaPath = alphaPath ;
97- BetaName = betaName ;
98- BetaPath = betaPath ;
99- StatusCategory = statusCategory ;
100- StatusString = statusString ;
101- StatusDescription = statusDescription ;
102- AlphaSize = new SyncSessionModelEndpointSize
103- {
104- SizeBytes = ( ulong ) new Random ( ) . Next ( 0 , 1000000000 ) ,
105- FileCount = ( ulong ) new Random ( ) . Next ( 0 , 10000 ) ,
106- DirCount = ( ulong ) new Random ( ) . Next ( 0 , 10000 ) ,
107- } ;
108- BetaSize = new SyncSessionModelEndpointSize
109- {
110- SizeBytes = ( ulong ) new Random ( ) . Next ( 0 , 1000000000 ) ,
111- FileCount = ( ulong ) new Random ( ) . Next ( 0 , 10000 ) ,
112- DirCount = ( ulong ) new Random ( ) . Next ( 0 , 10000 ) ,
113- } ;
114-
115- Errors = errors ;
116- }
117-
118100 public SyncSessionModel ( State state )
119101 {
120102 Identifier = state . Session . Identifier ;
121- Name = state . Session . Name ;
103+ CreatedAt = state . Session . CreationTime . ToDateTime ( ) ;
122104
123105 ( AlphaName , AlphaPath ) = NameAndPathFromUrl ( state . Session . Alpha ) ;
124106 ( BetaName , BetaPath ) = NameAndPathFromUrl ( state . Session . Beta ) ;
@@ -220,6 +202,9 @@ public SyncSessionModel(State state)
220202 StatusDescription = "The session has conflicts that need to be resolved." ;
221203 }
222204
205+ Conflicts = state . Conflicts . Select ( ConflictToString ) . ToList ( ) ;
206+ OmittedConflicts = state . ExcludedConflicts ;
207+
223208 AlphaSize = new SyncSessionModelEndpointSize
224209 {
225210 SizeBytes = state . AlphaState . TotalFileSize ,
@@ -235,9 +220,24 @@ public SyncSessionModel(State state)
235220 SymlinkCount = state . BetaState . SymbolicLinks ,
236221 } ;
237222
238- // TODO: accumulate errors, there seems to be multiple fields they can
239- // come from
240- if ( ! string . IsNullOrWhiteSpace ( state . LastError ) ) Errors = [ state . LastError ] ;
223+ List < string > errors = [ ] ;
224+ if ( ! string . IsNullOrWhiteSpace ( state . LastError ) ) errors . Add ( $ "Last error:\n { state . LastError } ") ;
225+ // TODO: scan problems + transition problems + omissions should probably be fields
226+ foreach ( var scanProblem in state . AlphaState . ScanProblems ) errors . Add ( $ "Alpha scan problem: { scanProblem } ") ;
227+ if ( state . AlphaState . ExcludedScanProblems > 0 )
228+ errors . Add ( $ "Alpha scan problems omitted: { state . AlphaState . ExcludedScanProblems } ") ;
229+ foreach ( var scanProblem in state . AlphaState . ScanProblems ) errors . Add ( $ "Beta scan problem: { scanProblem } ") ;
230+ if ( state . BetaState . ExcludedScanProblems > 0 )
231+ errors . Add ( $ "Beta scan problems omitted: { state . BetaState . ExcludedScanProblems } ") ;
232+ foreach ( var transitionProblem in state . AlphaState . TransitionProblems )
233+ errors . Add ( $ "Alpha transition problem: { transitionProblem } ") ;
234+ if ( state . AlphaState . ExcludedTransitionProblems > 0 )
235+ errors . Add ( $ "Alpha transition problems omitted: { state . AlphaState . ExcludedTransitionProblems } ") ;
236+ foreach ( var transitionProblem in state . AlphaState . TransitionProblems )
237+ errors . Add ( $ "Beta transition problem: { transitionProblem } ") ;
238+ if ( state . BetaState . ExcludedTransitionProblems > 0 )
239+ errors . Add ( $ "Beta transition problems omitted: { state . BetaState . ExcludedTransitionProblems } ") ;
240+ Errors = errors ;
241241 }
242242
243243 private static ( string , string ) NameAndPathFromUrl ( URL url )
@@ -251,4 +251,55 @@ private static (string, string) NameAndPathFromUrl(URL url)
251251
252252 return ( name , path ) ;
253253 }
254+
255+ private static string ConflictToString ( Conflict conflict )
256+ {
257+ string ? friendlyProblem = null ;
258+ if ( conflict . AlphaChanges . Count == 1 && conflict . BetaChanges . Count == 1 &&
259+ conflict . AlphaChanges [ 0 ] . Old == null &&
260+ conflict . BetaChanges [ 0 ] . Old == null &&
261+ conflict . AlphaChanges [ 0 ] . New != null &&
262+ conflict . BetaChanges [ 0 ] . New != null )
263+ friendlyProblem =
264+ "An entry was created on both endpoints and they do not match. You can resolve this conflict by deleting one of the entries on either side." ;
265+
266+ var str = $ "Conflict at path '{ conflict . Root } ':";
267+ foreach ( var change in conflict . AlphaChanges )
268+ str += $ "\n (alpha) { ChangeToString ( change ) } ";
269+ foreach ( var change in conflict . BetaChanges )
270+ str += $ "\n (beta) { ChangeToString ( change ) } ";
271+ if ( friendlyProblem != null )
272+ str += $ "\n \n { friendlyProblem } ";
273+
274+ return str ;
275+ }
276+
277+ private static string ChangeToString ( Change change )
278+ {
279+ return $ "{ change . Path } ({ EntryToString ( change . Old ) } -> { EntryToString ( change . New ) } )";
280+ }
281+
282+ private static string EntryToString ( Entry ? entry )
283+ {
284+ if ( entry == null ) return "<non-existent>" ;
285+ var str = entry . Kind . ToString ( ) ;
286+ switch ( entry . Kind )
287+ {
288+ case EntryKind . Directory :
289+ str += $ " ({ entry . Contents . Count } entries)";
290+ break ;
291+ case EntryKind . File :
292+ var digest = BitConverter . ToString ( entry . Digest . ToByteArray ( ) ) . Replace ( "-" , "" ) . ToLower ( ) ;
293+ str += $ " ({ digest } , executable: { entry . Executable } )";
294+ break ;
295+ case EntryKind . SymbolicLink :
296+ str += $ " (target: { entry . Target } )";
297+ break ;
298+ case EntryKind . Problematic :
299+ str += $ " ({ entry . Problem } )";
300+ break ;
301+ }
302+
303+ return str ;
304+ }
254305}
0 commit comments