Skip to content

Commit 7d51734

Browse files
committed
WIP! Thu Oct 5 01:14:09 CEST 2023
1 parent 18ed447 commit 7d51734

File tree

4 files changed

+155
-56
lines changed

4 files changed

+155
-56
lines changed

AeroSpace.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
45AA5FD4A023AF751922BC22 /* BundleEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B7A2DF0D1F72B80B1F04240 /* BundleEx.swift */; };
2626
45EA2D1C90430C432E123B51 /* keysMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1C0D40CBD65704BA9595C2FA /* keysMap.swift */; };
2727
56E72B24303F5F337B31B776 /* TrayMenuModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F5F52E346D024960EAF5938 /* TrayMenuModel.swift */; };
28+
598F12005E4D15FF1A479181 /* ResultEx.swift in Sources */ = {isa = PBXBuildFile; fileRef = 598F185C1BAF2E829B5164CC /* ResultEx.swift */; };
2829
5DA2DA21600E8B5BCA3DCFC0 /* LayoutCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A2B673C67B00DBFCC27FFE7 /* LayoutCommand.swift */; };
2930
6317AB471F4C4F5D66A25784 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EEDBFFCA7A77D96B18FB0732 /* Assets.xcassets */; };
3031
635733FDDF37E44364372B74 /* MruStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 954A434EE57D76F5A9D4140D /* MruStack.swift */; };
@@ -98,6 +99,7 @@
9899
5274C575044C2A7123C57584 /* AeroSpace-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "AeroSpace-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
99100
53FDECECC773EBA30661EB8A /* FlattenWorkspaceTreeCommandTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlattenWorkspaceTreeCommandTest.swift; sourceTree = "<group>"; };
100101
569422C0C4C23EF3E024C8E6 /* ExecAndForgetCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExecAndForgetCommand.swift; sourceTree = "<group>"; };
102+
598F185C1BAF2E829B5164CC /* ResultEx.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResultEx.swift; sourceTree = "<group>"; };
101103
5F5F52E346D024960EAF5938 /* TrayMenuModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrayMenuModel.swift; sourceTree = "<group>"; };
102104
6352ADEE6625D9703CFCA99A /* Window.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Window.swift; sourceTree = "<group>"; };
103105
67B9FFF81EB0327ABD51A7FE /* MoveThroughCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoveThroughCommand.swift; sourceTree = "<group>"; };
@@ -301,6 +303,7 @@
301303
28B788A95DD3C267878E05B5 /* Rect.swift */,
302304
AAE5DCAEC5EE619CE33859E7 /* SequenceEx.swift */,
303305
976540EBEACF846D598CD6E1 /* util.swift */,
306+
598F185C1BAF2E829B5164CC /* ResultEx.swift */,
304307
);
305308
path = util;
306309
sourceTree = "<group>";
@@ -461,6 +464,7 @@
461464
B3702BB393A9B03CCAE4C60E /* refresh.swift in Sources */,
462465
ED96E36786C941AB3AF780BC /* startAtLogin.swift in Sources */,
463466
93D44EA41776738B4758C28D /* util.swift in Sources */,
467+
598F12005E4D15FF1A479181 /* ResultEx.swift in Sources */,
464468
);
465469
runOnlyForDeploymentPostprocessing = 0;
466470
};

src/command/parseCommand.swift

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import TOMLKit
22

3+
typealias ParsedCommand<T> = Result<T, String>
4+
extension String: Error {}
5+
36
// todo drop TomlBacktrace
47
func parseCommand(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> Command {
58
if let rawString = raw.string {
@@ -40,7 +43,7 @@ private func parseSingleCommand(_ raw: String, _ backtrace: TomlBacktrace) -> Co
4043
?? errorT("\(backtrace): Can't parse '\(firstWord)' direction")
4144
return MoveThroughCommand(direction: direction)
4245
} else if firstWord == "layout" {
43-
return LayoutCommand(toggleBetween: args.map { parseLayout(String($0), backtrace) })
46+
return LayoutCommand(toggleBetween: args.map { parseLayout(String($0)) })
4447
?? errorT("\(backtrace): Can't create layout command") // todo nicer message
4548
} else if raw == "workspace-back-and-forth" {
4649
return WorkspaceBackAndForthCommand()
@@ -57,8 +60,8 @@ private func parseSingleCommand(_ raw: String, _ backtrace: TomlBacktrace) -> Co
5760
}
5861
}
5962

60-
func parseLayout(_ raw: String, _ backtrace: TomlBacktrace) -> ConfigLayout {
61-
ConfigLayout(rawValue: raw) ?? errorT("\(backtrace): Can't parse layout '\(raw)'")
63+
func parseLayout(_ raw: String) -> ParsedCommand<ConfigLayout> {
64+
ConfigLayout(rawValue: raw).orFailure { "Can't parse layout '\(raw)'" }
6265
}
6366

6467
private func parseSingleArg(_ args: ArraySlice<Swift.String.SubSequence>, _ command: String, _ backtrace: TomlBacktrace) -> String {

src/config/parseConfig.swift

Lines changed: 137 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,56 @@ func reloadConfig() {
1010
syncStartAtLogin()
1111
}
1212

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> {
1463
let rawTable: TOMLTable
1564
do {
1665
rawTable = try TOMLTable(string: rawToml)
@@ -21,6 +70,7 @@ func parseConfig(_ rawToml: String) -> Config {
2170
}
2271

2372
var modes: [String: Mode]? = nil
73+
var errors: [TomlParseError] = []
2474

2575
let key1 = "after-startup-command"
2676
var value1: Command? = nil
@@ -61,25 +111,25 @@ func parseConfig(_ rawToml: String) -> Config {
61111
case key1:
62112
value1 = parseCommand(value, backtrace)
63113
case key2:
64-
value2 = parseBool(value, backtrace)
114+
(value2, errors) = parseBool(value, backtrace).unwrap(errors)
65115
case key3:
66-
value3 = parseBool(value, backtrace)
116+
(value3, errors) = parseBool(value, backtrace).unwrap(errors)
67117
case key4:
68-
value4 = parseBool(value, backtrace)
118+
(value4, errors) = parseBool(value, backtrace).unwrap(errors)
69119
case key5:
70-
value5 = parseMainLayout(value, backtrace)
120+
(value5, errors) = parseMainLayout(value, backtrace).unwrap(errors)
71121
case key6:
72-
value6 = parseFocusWrapping(value, backtrace)
122+
(value6, errors) = parseFocusWrapping(value, backtrace).unwrap(errors)
73123
case key7:
74-
value7 = parseBool(value, backtrace)
124+
(value7, errors) = parseBool(value, backtrace).unwrap(errors)
75125
case key8:
76-
value8 = parseBool(value, backtrace)
126+
(value8, errors) = parseBool(value, backtrace).unwrap(errors)
77127
case key9:
78128
value9 = parseCommand(value, backtrace)
79129
case key10:
80-
value10 = parseTrayIconContent(value, backtrace)
130+
(value10, errors) = parseTrayIconContent(value, backtrace).unwrap(errors)
81131
case key11:
82-
value11 = parseString(value, backtrace)
132+
(value11, errors) = parseString(value, backtrace).unwrap(errors)
83133
case "mode":
84134
modes = parseModes(value, backtrace)
85135
default:
@@ -110,28 +160,36 @@ func parseConfig(_ rawToml: String) -> Config {
110160
)
111161
}
112162

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) }
116165
}
117166

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+
}
120171
}
121172

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+
}
124177
}
125178

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+
}
132185
}
133186

134187
private func parseModes(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> [String: Mode] {
188+
raw.table.orFailure { expectedActualTypeError(expected: .table, actual: raw.type, backtrace) }
189+
190+
191+
192+
135193
let rawTable = raw.table ?? expectedActualTypeError(expected: .table, actual: raw.type, backtrace)
136194
var result: [String: Mode] = [:]
137195
for (key, value) in rawTable {
@@ -143,28 +201,31 @@ private func parseModes(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace)
143201
return result
144202
}
145203

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+
)
159225
}
160-
}
161-
return Mode(
162-
name: nil,
163-
bindings: value1
164-
)
165226
}
166227

167-
private func parseBindings(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> [HotkeyBinding] {
228+
private func parseBindings(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktrace) -> ParsedTomlWriter<[HotkeyBinding]> {
168229
let rawTable = raw.table ?? expectedActualTypeError(expected: .table, actual: raw.type, backtrace)
169230
return rawTable.map { (binding: String, value: TOMLValueConvertible) in
170231
let keyBacktrace = backtrace + .key(binding)
@@ -173,16 +234,39 @@ private func parseBindings(_ raw: TOMLValueConvertible, _ backtrace: TomlBacktra
173234
}
174235
}
175236

176-
private func parseBinding(_ raw: String, _ backtrace: TomlBacktrace) -> (NSEvent.ModifierFlags, Key) {
237+
private func parseBinding(_ raw: String, _ backtrace: TomlBacktrace) -> ParsedTomlResult<(NSEvent.ModifierFlags, Key)> {
177238
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+
}
182266
}
183267

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) }
186270
}
187271

188272
// todo make private
@@ -210,10 +294,10 @@ indirect enum TomlBacktrace: CustomStringConvertible {
210294
}
211295
}
212296

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)'")
215299
}
216300

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)'")
219303
}

src/util/OptionalEx.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
extension Optional {
22
func orElse(_ other: () -> Wrapped) -> Wrapped { self ?? other() }
3+
4+
func orFailure<F: Error>(_ or: () -> F) -> Result<Wrapped, F> {
5+
if let ok = self {
6+
return .success(ok)
7+
} else {
8+
return .failure(or())
9+
}
10+
}
311
}

0 commit comments

Comments
 (0)