diff --git a/MTMR.xcodeproj/project.pbxproj b/MTMR.xcodeproj/project.pbxproj index ed288e9..d2079ec 100644 --- a/MTMR.xcodeproj/project.pbxproj +++ b/MTMR.xcodeproj/project.pbxproj @@ -9,6 +9,9 @@ /* Begin PBXBuildFile section */ 36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */; }; 36C2ECD9207B74B4003CDA33 /* AppleScriptTouchBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */; }; + 36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; }; + 36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */; }; + 36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */; }; B059D622205E03F5006E6B86 /* TouchBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D621205E03F5006E6B86 /* TouchBarController.swift */; }; B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = B059D623205E04F3006E6B86 /* TouchBarItems.swift */; }; B059D62D205F11E8006E6B86 /* DFRFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B059D62C205F11E8006E6B86 /* DFRFoundation.framework */; }; @@ -16,7 +19,6 @@ B082B255205C7D8000BC04DC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B254205C7D8000BC04DC /* ViewController.swift */; }; B082B257205C7D8000BC04DC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B082B256205C7D8000BC04DC /* Assets.xcassets */; }; B082B25A205C7D8000BC04DC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B082B258205C7D8000BC04DC /* Main.storyboard */; }; - B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B265205C7D8000BC04DC /* MTMRTests.swift */; }; B082B271205C7D8000BC04DC /* MTMRUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B082B270205C7D8000BC04DC /* MTMRUITests.swift */; }; B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */ = {isa = PBXBuildFile; fileRef = B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */; }; B09EB1E6207C0F8E00D5C1E0 /* MultitouchSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B09EB1E5207C0F8E00D5C1E0 /* MultitouchSupport.framework */; }; @@ -48,6 +50,8 @@ 36C2ECD2207B3B1D003CDA33 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 36C2ECD6207B6DAE003CDA33 /* TimeTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeTouchBarItem.swift; sourceTree = ""; }; 36C2ECD8207B74B4003CDA33 /* AppleScriptTouchBarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleScriptTouchBarItem.swift; sourceTree = ""; }; + 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsParsing.swift; sourceTree = ""; }; + 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfigTests.swift; sourceTree = ""; }; B059D621205E03F5006E6B86 /* TouchBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarController.swift; sourceTree = ""; }; B059D623205E04F3006E6B86 /* TouchBarItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouchBarItems.swift; sourceTree = ""; }; B059D629205E13E5006E6B86 /* TouchBarPrivateApi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TouchBarPrivateApi.h; sourceTree = ""; }; @@ -61,7 +65,6 @@ B082B25B205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B082B25C205C7D8000BC04DC /* MTMR.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MTMR.entitlements; sourceTree = ""; }; B082B261205C7D8000BC04DC /* MTMRTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - B082B265205C7D8000BC04DC /* MTMRTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRTests.swift; sourceTree = ""; }; B082B267205C7D8000BC04DC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; B082B26C205C7D8000BC04DC /* MTMRUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MTMRUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; B082B270205C7D8000BC04DC /* MTMRUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MTMRUITests.swift; sourceTree = ""; }; @@ -156,6 +159,7 @@ B0F8771C207AD35400D6E430 /* battery.scpt */, B0A8BF9D207B84160086F74D /* weather.scpt */, B09EB1E3207C082000D5C1E0 /* HapticFeedback.swift */, + 36C2ECDA207C3FE7003CDA33 /* ItemsParsing.swift */, ); path = MTMR; sourceTree = ""; @@ -163,7 +167,7 @@ B082B264205C7D8000BC04DC /* MTMRTests */ = { isa = PBXGroup; children = ( - B082B265205C7D8000BC04DC /* MTMRTests.swift */, + 36C2ECDC207C723B003CDA33 /* ParseConfigTests.swift */, B082B267205C7D8000BC04DC /* Info.plist */, ); path = MTMRTests; @@ -326,6 +330,7 @@ B082B253205C7D8000BC04DC /* AppDelegate.swift in Sources */, B059D624205E04F3006E6B86 /* TouchBarItems.swift in Sources */, B09EB1E4207C082000D5C1E0 /* HapticFeedback.swift in Sources */, + 36C2ECDB207C3FE7003CDA33 /* ItemsParsing.swift in Sources */, B0A7E9AA205D6AA400EEF070 /* KeyPress.swift in Sources */, 36C2ECD7207B6DAE003CDA33 /* TimeTouchBarItem.swift in Sources */, ); @@ -335,7 +340,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B082B266205C7D8000BC04DC /* MTMRTests.swift in Sources */, + 36C2ECDD207C723B003CDA33 /* ParseConfigTests.swift in Sources */, + 36C2ECDE207C82DE003CDA33 /* ItemsParsing.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/MTMR/ItemsParsing.swift b/MTMR/ItemsParsing.swift new file mode 100644 index 0000000..fffbee4 --- /dev/null +++ b/MTMR/ItemsParsing.swift @@ -0,0 +1,137 @@ +import Foundation + +struct BarItemDefinition: Decodable { + let type: ItemType + let action: ActionType + + private enum CodingKeys: String, CodingKey { + case type + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let type = try container.decode(String.self, forKey: .type) + let parametersDecoder = SupportedTypesHolder.sharedInstance.lookup(by: type) + if let result = try? parametersDecoder(decoder), + case let (itemType, action) = result { + self.type = itemType + self.action = action + } else { + self.type = .staticButton(title: "unknown") + self.action = .none + } + } + +} + +class SupportedTypesHolder { + typealias ParametersDecoder = (Decoder) throws ->(item: ItemType, action: ActionType) + private var supportedTypes: [String: ParametersDecoder] = [ + "brightnessUp": { _ in return (item: .staticButton(title: "🔆"), action: .keyPress(keycode: 113)) }, + ] + + static let sharedInstance = SupportedTypesHolder() + + func lookup(by type: String) -> ParametersDecoder { + if let extraType = supportedTypes[type] { + return extraType + } else { + return { decoder in + return (item: try ItemType(from: decoder), action: try ActionType(from: decoder)) + } + } + } +} + +enum ItemType: Decodable { + case staticButton(title: String) + case appleScriptTitledButton(source: String) + + private enum CodingKeys: String, CodingKey { + case type + case title + case titleAppleScript + } + + private enum ItemTypeRaw: String, Decodable { + case staticButton + case appleScriptTitledButton + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let type = try container.decode(ItemTypeRaw.self, forKey: .type) + switch type { + case .appleScriptTitledButton: + let source = try container.decode(String.self, forKey: .titleAppleScript) + self = .appleScriptTitledButton(source: source) + case .staticButton: + let title = try container.decode(String.self, forKey: .title) + self = .staticButton(title: title) + } + } +} + +enum ActionType: Decodable { + case none + case hidKey(keycode: Int) + case keyPress(keycode: Int) + case appleSctipt(source: String) + + private enum CodingKeys: String, CodingKey { + case action + case keycode + case actionAppleScript + } + + private enum ActionTypeRaw: String, Decodable { + case hidKey + case keyPress + case appleScript + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let type = try container.decodeIfPresent(ActionTypeRaw.self, forKey: .action) + switch type { + case .some(.hidKey): + let keycode = try container.decode(Int.self, forKey: .keycode) + self = .hidKey(keycode: keycode) + case .some(.keyPress): + let keycode = try container.decode(Int.self, forKey: .keycode) + self = .keyPress(keycode: keycode) + case .some(.appleScript): + let source = try container.decode(String.self, forKey: .actionAppleScript) + self = .appleSctipt(source: source) + case .none: + self = .none + } + } +} + +extension ItemType: Equatable {} +func ==(lhs: ItemType, rhs: ItemType) -> Bool { + switch (lhs, rhs) { + case let (.staticButton(a), .staticButton(b)), + let (.appleScriptTitledButton(a), .appleScriptTitledButton(b)): + return a == b + default: + return false + } +} + +extension ActionType: Equatable {} +func ==(lhs: ActionType, rhs: ActionType) -> Bool { + switch (lhs, rhs) { + case (.none, .none): + return true + case let (.hidKey(a), .hidKey(b)), + let (.keyPress(a), .keyPress(b)): + return a == b + case let (.appleSctipt(a), .appleSctipt(b)): + return a == b + default: + return false + } +} + diff --git a/MTMRTests/MTMRTests.swift b/MTMRTests/MTMRTests.swift deleted file mode 100644 index aeab5e1..0000000 --- a/MTMRTests/MTMRTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// MTMRTests.swift -// MTMRTests -// -// Created by Anton Palgunov on 16/03/2018. -// Copyright © 2018 Anton Palgunov. All rights reserved. -// - -import XCTest -@testable import MTMR - -class MTMRTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - func testExample() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/MTMRTests/ParseConfigTests.swift b/MTMRTests/ParseConfigTests.swift new file mode 100644 index 0000000..bd29393 --- /dev/null +++ b/MTMRTests/ParseConfigTests.swift @@ -0,0 +1,33 @@ +import XCTest +@testable import MTMR + +class ParseConfig: XCTestCase { + + func testButtonNoAction() { + let buttonNoActionFixture = """ + [ { "type": "staticButton", "title": "Pew" } ] + """.data(using: .utf8)! + let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonNoActionFixture) + XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew")) + XCTAssertEqual(result?.first?.action, .some(.none)) + } + + func testButtonKeyCodeAction() { + let buttonKeycodeFixture = """ + [ { "type": "staticButton", "title": "Pew", "action": "hidKey", "keycode": 123} ] + """.data(using: .utf8)! + let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture) + XCTAssertEqual(result?.first?.type, .staticButton(title: "Pew")) + XCTAssertEqual(result?.first?.action, .hidKey(keycode: 123)) + } + + func testPredefinedItem() { + let buttonKeycodeFixture = """ + [ { "type": "brightnessUp" } ] + """.data(using: .utf8)! + let result = try? JSONDecoder().decode([BarItemDefinition].self, from: buttonKeycodeFixture) + XCTAssertEqual(result?.first?.type, .staticButton(title: "🔆")) + XCTAssertEqual(result?.first?.action, .keyPress(keycode: 113)) + } + +}