@@ -2,21 +2,142 @@ import SwiftUI
22
33public struct FileSyncSession : Identifiable {
44 public let id : String
5- public let localPath : URL
6- public let workspace : String
7- // This is a string as to be host-OS agnostic
5+ public let name : String
6+
7+ public let localPath : String
8+ public let agentHost : String
89 public let remotePath : String
910 public let status : FileSyncStatus
10- public let size : String
11+
12+ public let maxSize : FileSyncSessionEndpointSize
13+ public let localSize : FileSyncSessionEndpointSize
14+ public let remoteSize : FileSyncSessionEndpointSize
15+
16+ public let errors : [ FileSyncError ]
17+
18+ init ( state: Synchronization_State ) {
19+ id = state. session. identifier
20+ name = state. session. name
21+
22+ // If the protocol isn't what we expect for alpha or beta, show unknown
23+ localPath = if state. session. alpha. protocol == Url_Protocol . local, !state. session. alpha. path. isEmpty {
24+ state. session. alpha. path
25+ } else {
26+ " Unknown "
27+ }
28+ if state. session. beta. protocol == Url_Protocol . ssh, !state. session. beta. host. isEmpty {
29+ let host = state. session. beta. host
30+ // TOOD: We need to either:
31+ // - make this compatible with custom suffixes
32+ // - always strip the tld
33+ // - always keep the tld
34+ agentHost = host. hasSuffix ( " .coder " ) ? String ( host. dropLast ( 6 ) ) : host
35+ } else {
36+ agentHost = " Unknown "
37+ }
38+ remotePath = if !state. session. beta. path. isEmpty {
39+ state. session. beta. path
40+ } else {
41+ " Unknown "
42+ }
43+
44+ var status : FileSyncStatus = if state. session. paused {
45+ . paused
46+ } else {
47+ convertSessionStatus ( status: state. status)
48+ }
49+ if case . error = status { } else {
50+ if state. conflicts. count > 0 {
51+ status = . needsAttention( name: " Conflicts " , desc: " The session has conflicts that need to be resolved " )
52+ }
53+ }
54+ self . status = status
55+
56+ localSize = . init(
57+ sizeBytes: state. alphaState. totalFileSize,
58+ fileCount: state. alphaState. files,
59+ dirCount: state. alphaState. directories,
60+ symLinkCount: state. alphaState. symbolicLinks
61+ )
62+ remoteSize = . init(
63+ sizeBytes: state. betaState. totalFileSize,
64+ fileCount: state. betaState. files,
65+ dirCount: state. betaState. directories,
66+ symLinkCount: state. betaState. symbolicLinks
67+ )
68+ maxSize = localSize. maxOf ( other: remoteSize)
69+
70+ errors = accumulateErrors ( from: state)
71+ }
72+
73+ public var statusAndErrors : String {
74+ var out = " \( status. type) \n \n \( status. description) "
75+ errors. forEach { out += " \n \t \( $0) " }
76+ return out
77+ }
78+
79+ public var sizeDescription : String {
80+ var out = " "
81+ if localSize != remoteSize {
82+ out += " Maximum: \n \( maxSize. description ( linePrefix: " " ) ) \n \n "
83+ }
84+ out += " Local: \n \( localSize. description ( linePrefix: " " ) ) \n \n "
85+ out += " Remote: \n \( remoteSize. description ( linePrefix: " " ) ) "
86+ return out
87+ }
88+ }
89+
90+ public struct FileSyncSessionEndpointSize : Equatable {
91+ public let sizeBytes : UInt64
92+ public let fileCount : UInt64
93+ public let dirCount : UInt64
94+ public let symLinkCount : UInt64
95+
96+ public init ( sizeBytes: UInt64 , fileCount: UInt64 , dirCount: UInt64 , symLinkCount: UInt64 ) {
97+ self . sizeBytes = sizeBytes
98+ self . fileCount = fileCount
99+ self . dirCount = dirCount
100+ self . symLinkCount = symLinkCount
101+ }
102+
103+ func maxOf( other: FileSyncSessionEndpointSize ) -> FileSyncSessionEndpointSize {
104+ FileSyncSessionEndpointSize (
105+ sizeBytes: max ( sizeBytes, other. sizeBytes) ,
106+ fileCount: max ( fileCount, other. fileCount) ,
107+ dirCount: max ( dirCount, other. dirCount) ,
108+ symLinkCount: max ( symLinkCount, other. symLinkCount)
109+ )
110+ }
111+
112+ public var humanSizeBytes : String {
113+ humanReadableBytes ( sizeBytes)
114+ }
115+
116+ public func description( linePrefix: String = " " ) -> String {
117+ var result = " "
118+ result += linePrefix + humanReadableBytes( sizeBytes) + " \n "
119+ let numberFormatter = NumberFormatter ( )
120+ numberFormatter. numberStyle = . decimal
121+ if let formattedFileCount = numberFormatter. string ( from: NSNumber ( value: fileCount) ) {
122+ result += " \( linePrefix) \( formattedFileCount) file \( fileCount == 1 ? " " : " s " ) \n "
123+ }
124+ if let formattedDirCount = numberFormatter. string ( from: NSNumber ( value: dirCount) ) {
125+ result += " \( linePrefix) \( formattedDirCount) director \( dirCount == 1 ? " y " : " ies " ) "
126+ }
127+ if symLinkCount > 0 , let formattedSymLinkCount = numberFormatter. string ( from: NSNumber ( value: symLinkCount) ) {
128+ result += " \n \( linePrefix) \( formattedSymLinkCount) symlink \( symLinkCount == 1 ? " " : " s " ) "
129+ }
130+ return result
131+ }
11132}
12133
13134public enum FileSyncStatus {
14135 case unknown
15- case error( String )
136+ case error( name : String , desc : String )
16137 case ok
17138 case paused
18- case needsAttention( String )
19- case working( String )
139+ case needsAttention( name : String , desc : String )
140+ case working( name : String , desc : String )
20141
21142 public var color : Color {
22143 switch self {
@@ -31,28 +152,69 @@ public enum FileSyncStatus {
31152 case . needsAttention:
32153 . orange
33154 case . working:
34- . white
155+ . purple
35156 }
36157 }
37158
38- public var description : String {
159+ public var type : String {
39160 switch self {
40161 case . unknown:
41162 " Unknown "
42- case let . error( msg ) :
43- msg
163+ case let . error( name , _ ) :
164+ " \( name ) "
44165 case . ok:
45166 " Watching "
46167 case . paused:
47168 " Paused "
48- case let . needsAttention( msg ) :
49- msg
50- case let . working( msg ) :
51- msg
169+ case let . needsAttention( name , _ ) :
170+ name
171+ case let . working( name , _ ) :
172+ name
52173 }
53174 }
54175
55- public var body : some View {
56- Text ( description) . foregroundColor ( color)
176+ public var description : String {
177+ switch self {
178+ case . unknown:
179+ " Unknown status message. "
180+ case let . error( _, desc) :
181+ desc
182+ case . ok:
183+ " The session is watching for filesystem changes. "
184+ case . paused:
185+ " The session is paused. "
186+ case let . needsAttention( _, desc) :
187+ desc
188+ case let . working( _, desc) :
189+ desc
190+ }
191+ }
192+
193+ public var column : some View {
194+ Text ( type) . foregroundColor ( color)
195+ }
196+ }
197+
198+ public enum FileSyncEndpoint {
199+ case local
200+ case remote
201+ }
202+
203+ public enum FileSyncProblemType {
204+ case scan
205+ case transition
206+ }
207+
208+ public enum FileSyncError {
209+ case generic( String )
210+ case problem( FileSyncEndpoint , FileSyncProblemType , path: String , error: String )
211+
212+ var description : String {
213+ switch self {
214+ case let . generic( error) :
215+ error
216+ case let . problem( endpoint, type, path, error) :
217+ " \( endpoint) \( type) error at \( path) : \( error) "
218+ }
57219 }
58220}
0 commit comments