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