@@ -10,7 +10,56 @@ func reloadConfig() {
10
10
syncStartAtLogin ( )
11
11
}
12
12
13
- func parseConfig( _ rawToml: String ) -> Config {
13
+ struct TomlParseError : Error {
14
+ let backtrace : TomlBacktrace
15
+ let message : String
16
+
17
+ init ( _ backtrace: TomlBacktrace , _ message: String ) {
18
+ self . backtrace = backtrace
19
+ self . message = message
20
+ }
21
+ }
22
+ //extension [TomlParseError]: Error {}
23
+
24
+ private typealias ParsedTomlResult < T> = Result < T , TomlParseError >
25
+
26
+ private extension Result {
27
+ func unwrap( _ existingErrors: [ Failure ] ) -> ( Success ? , [ Failure ] ) {
28
+ switch self {
29
+ case . success( let success) :
30
+ return ( success, existingErrors)
31
+ case . failure( let error) :
32
+ return ( nil , existingErrors + [ error] )
33
+ }
34
+ }
35
+ }
36
+
37
+ typealias ParsedTomlWriter < T> = Writer < T , TomlParseError >
38
+
39
+ struct Writer < T, L> {
40
+ let value : T
41
+ let log : [ L ]
42
+ }
43
+
44
+ //private struct Writer<T> { // Writer monad
45
+ // let data: T
46
+ // let errors: [ParseError]
47
+ // init(_ data: T, _ errors: [ParseError]) {
48
+ // self.data = data
49
+ // self.errors = errors
50
+ // }
51
+ // init(_ data: T) { self.init(data, []) }
52
+ //}
53
+
54
+ //extension Writer {
55
+ // func toTuple() -> (T, [ParseError]) { (data, errors) }
56
+ // func flatMap<R>(_ transformer: (T) -> Writer<R>) -> Writer<R> {
57
+ // let writer = transformer(data)
58
+ // return Writer<R>(writer.data, errors + writer.errors)
59
+ // }
60
+ //}
61
+
62
+ func parseConfig( _ rawToml: String ) -> ParsedTomlWriter < Config > {
14
63
let rawTable : TOMLTable
15
64
do {
16
65
rawTable = try TOMLTable ( string: rawToml)
@@ -21,6 +70,7 @@ func parseConfig(_ rawToml: String) -> Config {
21
70
}
22
71
23
72
var modes : [ String : Mode ] ? = nil
73
+ var errors : [ TomlParseError ] = [ ]
24
74
25
75
let key1 = " after-startup-command "
26
76
var value1 : Command ? = nil
@@ -61,25 +111,25 @@ func parseConfig(_ rawToml: String) -> Config {
61
111
case key1:
62
112
value1 = parseCommand ( value, backtrace)
63
113
case key2:
64
- value2 = parseBool ( value, backtrace)
114
+ ( value2, errors ) = parseBool ( value, backtrace) . unwrap ( errors )
65
115
case key3:
66
- value3 = parseBool ( value, backtrace)
116
+ ( value3, errors ) = parseBool ( value, backtrace) . unwrap ( errors )
67
117
case key4:
68
- value4 = parseBool ( value, backtrace)
118
+ ( value4, errors ) = parseBool ( value, backtrace) . unwrap ( errors )
69
119
case key5:
70
- value5 = parseMainLayout ( value, backtrace)
120
+ ( value5, errors ) = parseMainLayout ( value, backtrace) . unwrap ( errors )
71
121
case key6:
72
- value6 = parseFocusWrapping ( value, backtrace)
122
+ ( value6, errors ) = parseFocusWrapping ( value, backtrace) . unwrap ( errors )
73
123
case key7:
74
- value7 = parseBool ( value, backtrace)
124
+ ( value7, errors ) = parseBool ( value, backtrace) . unwrap ( errors )
75
125
case key8:
76
- value8 = parseBool ( value, backtrace)
126
+ ( value8, errors ) = parseBool ( value, backtrace) . unwrap ( errors )
77
127
case key9:
78
128
value9 = parseCommand ( value, backtrace)
79
129
case key10:
80
- value10 = parseTrayIconContent ( value, backtrace)
130
+ ( value10, errors ) = parseTrayIconContent ( value, backtrace) . unwrap ( errors )
81
131
case key11:
82
- value11 = parseString ( value, backtrace)
132
+ ( value11, errors ) = parseString ( value, backtrace) . unwrap ( errors )
83
133
case " mode " :
84
134
modes = parseModes ( value, backtrace)
85
135
default :
@@ -110,28 +160,36 @@ func parseConfig(_ rawToml: String) -> Config {
110
160
)
111
161
}
112
162
113
- private func parseString( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> String {
114
- let rawString = raw. string ?? expectedActualTypeError ( expected: . string, actual: raw. type, backtrace)
115
- return rawString
163
+ private func parseString( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < String > {
164
+ raw. string. orFailure { expectedActualTypeError ( expected: . string, actual: raw. type, backtrace) }
116
165
}
117
166
118
- private func parseTrayIconContent( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> TrayIconContent {
119
- TrayIconContent ( rawValue: parseString ( raw, backtrace) ) ?? errorT ( " \( backtrace) : Can't parse tray-icon-content " )
167
+ private func parseTrayIconContent( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < TrayIconContent > {
168
+ parseString ( raw, backtrace) . flatMap {
169
+ TrayIconContent ( rawValue: $0) . orFailure { TomlParseError ( backtrace, " Can't parse tray-icon-content " ) }
170
+ }
120
171
}
121
172
122
- private func parseFocusWrapping( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> FocusWrapping {
123
- FocusWrapping ( rawValue: parseString ( raw, backtrace) ) ?? errorT ( " \( backtrace) : Can't parse focus wrapping " )
173
+ private func parseFocusWrapping( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < FocusWrapping > {
174
+ parseString ( raw, backtrace) . flatMap {
175
+ FocusWrapping ( rawValue: $0) . orFailure { TomlParseError ( backtrace, " Can't parse focus wrapping " ) }
176
+ }
124
177
}
125
178
126
- private func parseMainLayout( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ConfigLayout {
127
- let layout = parseLayout ( parseString ( raw, backtrace ) , backtrace)
128
- if layout == . main {
129
- error ( " \( backtrace ) : main layout can't be ' \( layout ) ' " )
130
- }
131
- return layout
179
+ private func parseMainLayout( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < ConfigLayout > {
180
+ parseString ( raw, backtrace)
181
+ . flatMap { parseLayout ( $0 ) . mapError { TomlParseError ( backtrace , $0 ) } }
182
+ . flatMap { ( layout: ConfigLayout ) -> ParsedTomlResult < ConfigLayout > in
183
+ layout == . main ? . failure ( TomlParseError ( backtrace , " main layout can't be 'main' " ) ) : . success ( layout )
184
+ }
132
185
}
133
186
134
187
private func parseModes( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> [ String : Mode ] {
188
+ raw. table. orFailure { expectedActualTypeError ( expected: . table, actual: raw. type, backtrace) }
189
+
190
+
191
+
192
+
135
193
let rawTable = raw. table ?? expectedActualTypeError ( expected: . table, actual: raw. type, backtrace)
136
194
var result : [ String : Mode ] = [ : ]
137
195
for (key, value) in rawTable {
@@ -143,28 +201,31 @@ private func parseModes(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace)
143
201
return result
144
202
}
145
203
146
- private func parseMode( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> Mode {
147
- let rawTable = raw. table ?? expectedActualTypeError ( expected: . table, actual: raw. type, backtrace)
148
-
149
- let key1 = " binding "
150
- var value1 : [ HotkeyBinding ] = [ ]
151
-
152
- for (key, value) in rawTable {
153
- let keyBacktrace = backtrace + . key( key)
154
- switch key {
155
- case key1:
156
- value1 = parseBindings ( value, keyBacktrace)
157
- default :
158
- unknownKeyError ( keyBacktrace)
204
+ private func parseMode( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < Mode > {
205
+ raw. table. orFailure { expectedActualTypeError ( expected: . table, actual: raw. type, backtrace) }
206
+ . flatMap { ( rawTable: TOMLTable ) -> ParsedTomlResult < Mode > in
207
+ var errors : [ TomlParseError ] = [ ]
208
+
209
+ let key1 = " binding "
210
+ var value1 : [ HotkeyBinding ] ? = nil
211
+
212
+ for (key, value) in rawTable {
213
+ let keyBacktrace = backtrace + . key( key)
214
+ switch key {
215
+ case key1:
216
+ ( value1, errors) = parseBindings ( value, keyBacktrace) . unwrap ( errors)
217
+ default :
218
+ errors += [ unknownKeyError ( keyBacktrace) ]
219
+ }
220
+ }
221
+ return Mode (
222
+ name: nil ,
223
+ bindings: value1 ?? [ ]
224
+ )
159
225
}
160
- }
161
- return Mode (
162
- name: nil ,
163
- bindings: value1
164
- )
165
226
}
166
227
167
- private func parseBindings( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> [ HotkeyBinding ] {
228
+ private func parseBindings( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlWriter < [ HotkeyBinding ] > {
168
229
let rawTable = raw. table ?? expectedActualTypeError ( expected: . table, actual: raw. type, backtrace)
169
230
return rawTable. map { ( binding: String , value: TOMLValueConvertible ) in
170
231
let keyBacktrace = backtrace + . key( binding)
@@ -173,16 +234,39 @@ private func parseBindings(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktra
173
234
}
174
235
}
175
236
176
- private func parseBinding( _ raw: String , _ backtrace: TomlBacktrace ) -> ( NSEvent . ModifierFlags , Key ) {
237
+ private func parseBinding( _ raw: String , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < ( NSEvent . ModifierFlags , Key ) > {
177
238
let rawKeys = raw. split ( separator: " - " )
178
- let modifiers : [ NSEvent . ModifierFlags ] = rawKeys. dropLast ( )
179
- . map { modifiersMap [ String ( $0) ] ?? errorT ( " \( backtrace) : Can't parse ' \( raw) ' binding " ) }
180
- let key = rawKeys. last. flatMap { keysMap [ String ( $0) ] } ?? errorT ( " \( backtrace) : Can't parse ' \( raw) ' binding " )
181
- return ( NSEvent . ModifierFlags ( modifiers) , key)
239
+ let modifiers : ParsedTomlResult < NSEvent . ModifierFlags > = rawKeys. dropLast ( )
240
+ . mapOrFailure {
241
+ modifiersMap [ String ( $0) ] . orFailure { TomlParseError ( backtrace, " Can't parse modifiers in ' \( raw) ' binding " ) }
242
+ }
243
+ . map { NSEvent . ModifierFlags ( $0) }
244
+ let key : ParsedTomlResult < Key > = rawKeys. last. flatMap { keysMap [ String ( $0) ] }
245
+ . orFailure { TomlParseError ( backtrace, " Can't parse the key in ' \( raw) ' binding " ) }
246
+ return modifiers. flatMap { modifiers -> ParsedTomlResult < ( NSEvent . ModifierFlags , Key ) > in
247
+ key. flatMap { key -> ParsedTomlResult < ( NSEvent . ModifierFlags , Key ) > in
248
+ . success( ( modifiers, key) )
249
+ }
250
+ }
251
+ }
252
+
253
+ private extension Sequence {
254
+ func mapOrFailure< T, E> ( _ transform: ( Self . Element ) throws -> Result < T , E > ) rethrows -> Result < [ T ] , E > {
255
+ var result : [ T ] = [ ]
256
+ for element in self {
257
+ switch try transform ( element) {
258
+ case . success( let element) :
259
+ result. append ( element)
260
+ case . failure( let errors) :
261
+ return . failure( errors)
262
+ }
263
+ }
264
+ return . success( result)
265
+ }
182
266
}
183
267
184
- private func parseBool( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> Bool {
185
- raw. bool ?? expectedActualTypeError ( expected: . bool, actual: raw. type, backtrace)
268
+ private func parseBool( _ raw: TOMLValueConvertible , _ backtrace: TomlBacktrace ) -> ParsedTomlResult < Bool > {
269
+ raw. bool. orFailure { expectedActualTypeError ( expected: . bool, actual: raw. type, backtrace) }
186
270
}
187
271
188
272
// todo make private
@@ -210,10 +294,10 @@ indirect enum TomlBacktrace: CustomStringConvertible {
210
294
}
211
295
}
212
296
213
- private func unknownKeyError( _ backtrace: TomlBacktrace ) -> Never {
214
- error ( " Unknown key ' \( backtrace) ' " )
297
+ private func unknownKeyError( _ backtrace: TomlBacktrace ) -> TomlParseError {
298
+ TomlParseError ( backtrace , " Unknown key ' \( backtrace) ' " )
215
299
}
216
300
217
- private func expectedActualTypeError< T > ( expected: TOMLType , actual: TOMLType , _ backtrace: TomlBacktrace ) -> T {
218
- error ( " \( backtrace) : Expected type is '\( expected) '. But actual type is ' \( actual) ' " )
301
+ private func expectedActualTypeError( expected: TOMLType , actual: TOMLType , _ backtrace: TomlBacktrace ) -> TomlParseError {
302
+ TomlParseError ( backtrace, " Expected type is ' \( expected) '. But actual type is ' \( actual) ' " )
219
303
}
0 commit comments