@@ -3,19 +3,126 @@ import SwiftUI
33public struct FileSyncSession : Identifiable {
44 public let id : String
55 public let alphaPath : String
6+ public let name : String
7+
68 public let agentHost : String
79 public let betaPath : String
810 public let status : FileSyncStatus
9- public let size : String
11+
12+ public let localSize : FileSyncSessionEndpointSize
13+ public let remoteSize : FileSyncSessionEndpointSize
14+
15+ public let errors : [ FileSyncError ]
16+
17+ init ( state: Synchronization_State ) {
18+ id = state. session. identifier
19+ name = state. session. name
20+
21+ // If the protocol isn't what we expect for alpha or beta, show unknown
22+ alphaPath = if state. session. alpha. protocol == Url_Protocol . local, !state. session. alpha. path. isEmpty {
23+ state. session. alpha. path
24+ } else {
25+ " Unknown "
26+ }
27+ agentHost = if state. session. beta. protocol == Url_Protocol . ssh, !state. session. beta. host. isEmpty {
28+ // TOOD: We need to either:
29+ // - make this compatible with custom suffixes
30+ // - always strip the tld
31+ // - always keep the tld
32+ state. session. beta. host
33+ } else {
34+ " Unknown "
35+ }
36+ betaPath = if !state. session. beta. path. isEmpty {
37+ state. session. beta. path
38+ } else {
39+ " Unknown "
40+ }
41+
42+ var status : FileSyncStatus = if state. session. paused {
43+ . paused
44+ } else {
45+ convertSessionStatus ( status: state. status)
46+ }
47+ if case . error = status { } else {
48+ if state. conflicts. count > 0 {
49+ status = . conflicts
50+ }
51+ }
52+ self . status = status
53+
54+ localSize = . init(
55+ sizeBytes: state. alphaState. totalFileSize,
56+ fileCount: state. alphaState. files,
57+ dirCount: state. alphaState. directories,
58+ symLinkCount: state. alphaState. symbolicLinks
59+ )
60+ remoteSize = . init(
61+ sizeBytes: state. betaState. totalFileSize,
62+ fileCount: state. betaState. files,
63+ dirCount: state. betaState. directories,
64+ symLinkCount: state. betaState. symbolicLinks
65+ )
66+
67+ errors = accumulateErrors ( from: state)
68+ }
69+
70+ public var statusAndErrors : String {
71+ var out = " \( status. type) \n \n \( status. description) "
72+ errors. forEach { out += " \n \t \( $0) " }
73+ return out
74+ }
75+
76+ public var sizeDescription : String {
77+ var out = " "
78+ out += " Local: \n \( localSize. description ( linePrefix: " " ) ) \n \n "
79+ out += " Remote: \n \( remoteSize. description ( linePrefix: " " ) ) "
80+ return out
81+ }
82+ }
83+
84+ public struct FileSyncSessionEndpointSize : Equatable {
85+ public let sizeBytes : UInt64
86+ public let fileCount : UInt64
87+ public let dirCount : UInt64
88+ public let symLinkCount : UInt64
89+
90+ public init ( sizeBytes: UInt64 , fileCount: UInt64 , dirCount: UInt64 , symLinkCount: UInt64 ) {
91+ self . sizeBytes = sizeBytes
92+ self . fileCount = fileCount
93+ self . dirCount = dirCount
94+ self . symLinkCount = symLinkCount
95+ }
96+
97+ public var humanSizeBytes : String {
98+ humanReadableBytes ( sizeBytes)
99+ }
100+
101+ public func description( linePrefix: String = " " ) -> String {
102+ var result = " "
103+ result += linePrefix + humanReadableBytes( sizeBytes) + " \n "
104+ let numberFormatter = NumberFormatter ( )
105+ numberFormatter. numberStyle = . decimal
106+ if let formattedFileCount = numberFormatter. string ( from: NSNumber ( value: fileCount) ) {
107+ result += " \( linePrefix) \( formattedFileCount) file \( fileCount == 1 ? " " : " s " ) \n "
108+ }
109+ if let formattedDirCount = numberFormatter. string ( from: NSNumber ( value: dirCount) ) {
110+ result += " \( linePrefix) \( formattedDirCount) director \( dirCount == 1 ? " y " : " ies " ) "
111+ }
112+ if symLinkCount > 0 , let formattedSymLinkCount = numberFormatter. string ( from: NSNumber ( value: symLinkCount) ) {
113+ result += " \n \( linePrefix) \( formattedSymLinkCount) symlink \( symLinkCount == 1 ? " " : " s " ) "
114+ }
115+ return result
116+ }
10117}
11118
12119public enum FileSyncStatus {
13120 case unknown
14- case error( String )
121+ case error( FileSyncErrorStatus )
15122 case ok
16123 case paused
17- case needsAttention ( String )
18- case working( String )
124+ case conflicts
125+ case working( FileSyncWorkingStatus )
19126
20127 public var color : Color {
21128 switch self {
@@ -27,32 +134,164 @@ public enum FileSyncStatus {
27134 . red
28135 case . error:
29136 . red
30- case . needsAttention :
137+ case . conflicts :
31138 . orange
32139 case . working:
33- . white
140+ . purple
34141 }
35142 }
36143
37- public var description : String {
144+ public var type : String {
38145 switch self {
39146 case . unknown:
40147 " Unknown "
41- case let . error( msg ) :
42- msg
148+ case let . error( status ) :
149+ status . name
43150 case . ok:
44151 " Watching "
45152 case . paused:
46153 " Paused "
47- case let . needsAttention( msg) :
48- msg
49- case let . working( msg) :
50- msg
154+ case . conflicts:
155+ " Conflicts "
156+ case let . working( status) :
157+ status. name
158+ }
159+ }
160+
161+ public var description : String {
162+ switch self {
163+ case . unknown:
164+ " Unknown status message. "
165+ case let . error( status) :
166+ status. description
167+ case . ok:
168+ " The session is watching for filesystem changes. "
169+ case . paused:
170+ " The session is paused. "
171+ case . conflicts:
172+ " The session has conflicts that need to be resolved. "
173+ case let . working( status) :
174+ status. description
175+ }
176+ }
177+
178+ public var column : some View {
179+ Text ( type) . foregroundColor ( color)
180+ }
181+ }
182+
183+ public enum FileSyncWorkingStatus {
184+ case connectingAlpha
185+ case connectingBeta
186+ case scanning
187+ case reconciling
188+ case stagingAlpha
189+ case stagingBeta
190+ case transitioning
191+ case saving
192+
193+ var name : String {
194+ switch self {
195+ case . connectingAlpha:
196+ " Connecting (alpha) "
197+ case . connectingBeta:
198+ " Connecting (beta) "
199+ case . scanning:
200+ " Scanning "
201+ case . reconciling:
202+ " Reconciling "
203+ case . stagingAlpha:
204+ " Staging (alpha) "
205+ case . stagingBeta:
206+ " Staging (beta) "
207+ case . transitioning:
208+ " Transitioning "
209+ case . saving:
210+ " Saving "
211+ }
212+ }
213+
214+ var description : String {
215+ switch self {
216+ case . connectingAlpha:
217+ " The session is attempting to connect to the alpha endpoint. "
218+ case . connectingBeta:
219+ " The session is attempting to connect to the beta endpoint. "
220+ case . scanning:
221+ " The session is scanning the filesystem on each endpoint. "
222+ case . reconciling:
223+ " The session is performing reconciliation. "
224+ case . stagingAlpha:
225+ " The session is staging files on the alpha endpoint "
226+ case . stagingBeta:
227+ " The session is staging files on the beta endpoint "
228+ case . transitioning:
229+ " The session is performing transition operations on each endpoint. "
230+ case . saving:
231+ " The session is recording synchronization history to disk. "
51232 }
52233 }
234+ }
235+
236+ public enum FileSyncErrorStatus {
237+ case disconnected
238+ case haltedOnRootEmptied
239+ case haltedOnRootDeletion
240+ case haltedOnRootTypeChange
241+ case waitingForRescan
242+
243+ var name : String {
244+ switch self {
245+ case . disconnected:
246+ " Disconnected "
247+ case . haltedOnRootEmptied:
248+ " Halted on root emptied "
249+ case . haltedOnRootDeletion:
250+ " Halted on root deletion "
251+ case . haltedOnRootTypeChange:
252+ " Halted on root type change "
253+ case . waitingForRescan:
254+ " Waiting for rescan "
255+ }
256+ }
257+
258+ var description : String {
259+ switch self {
260+ case . disconnected:
261+ " The session is unpaused but not currently connected or connecting to either endpoint. "
262+ case . haltedOnRootEmptied:
263+ " The session is halted due to the root emptying safety check. "
264+ case . haltedOnRootDeletion:
265+ " The session is halted due to the root deletion safety check. "
266+ case . haltedOnRootTypeChange:
267+ " The session is halted due to the root type change safety check. "
268+ case . waitingForRescan:
269+ " The session is waiting to retry scanning after an error during the previous scan. "
270+ }
271+ }
272+ }
53273
54- public var body : some View {
55- Text ( description) . foregroundColor ( color)
274+ public enum FileSyncEndpoint {
275+ case local
276+ case remote
277+ }
278+
279+ public enum FileSyncProblemType {
280+ case scan
281+ case transition
282+ }
283+
284+ public enum FileSyncError {
285+ case generic( String )
286+ case problem( FileSyncEndpoint , FileSyncProblemType , path: String , error: String )
287+
288+ var description : String {
289+ switch self {
290+ case let . generic( error) :
291+ error
292+ case let . problem( endpoint, type, path, error) :
293+ " \( endpoint) \( type) error at \( path) : \( error) "
294+ }
56295 }
57296}
58297
0 commit comments