@@ -2,21 +2,142 @@ import SwiftUI
2
2
3
3
public struct FileSyncSession : Identifiable {
4
4
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
8
9
public let remotePath : String
9
10
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
+ }
11
132
}
12
133
13
134
public enum FileSyncStatus {
14
135
case unknown
15
- case error( String )
136
+ case error( name : String , desc : String )
16
137
case ok
17
138
case paused
18
- case needsAttention( String )
19
- case working( String )
139
+ case needsAttention( name : String , desc : String )
140
+ case working( name : String , desc : String )
20
141
21
142
public var color : Color {
22
143
switch self {
@@ -31,28 +152,69 @@ public enum FileSyncStatus {
31
152
case . needsAttention:
32
153
. orange
33
154
case . working:
34
- . white
155
+ . purple
35
156
}
36
157
}
37
158
38
- public var description : String {
159
+ public var type : String {
39
160
switch self {
40
161
case . unknown:
41
162
" Unknown "
42
- case let . error( msg ) :
43
- msg
163
+ case let . error( name , _ ) :
164
+ " \( name ) "
44
165
case . ok:
45
166
" Watching "
46
167
case . paused:
47
168
" 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
52
173
}
53
174
}
54
175
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
+ }
57
219
}
58
220
}
0 commit comments