From 234c050f898a9a11e7e167a3208c69b3d5a7ed8d Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Sun, 29 May 2022 19:58:15 +0100 Subject: [PATCH 01/14] Add CLI and Swift Package Manager Plugin --- .gitignore | 1 + Package.resolved | 51 +++++++------ Package.swift | 32 ++++++++- Plugins/LexiconCodeGeneratorPlugin/main.swift | 22 ++++++ Sources/KotlinStandAlone/Generator.swift | 3 +- Sources/Lexicon/CodeGenerator.swift | 1 + Sources/Lexicon/JSONClasses.swift | 3 +- Sources/SwiftLexicon/Generator.swift | 3 +- Sources/SwiftStandAlone/Generator.swift | 3 +- Sources/lexicon-generator/CodeGenerator.swift | 71 +++++++++++++++++++ 10 files changed, 161 insertions(+), 29 deletions(-) create mode 100644 Plugins/LexiconCodeGeneratorPlugin/main.swift create mode 100644 Sources/lexicon-generator/CodeGenerator.swift diff --git a/.gitignore b/.gitignore index 5466cb2..3b84a7c 100644 --- a/.gitignore +++ b/.gitignore @@ -90,3 +90,4 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ +.swiftpm/ diff --git a/Package.resolved b/Package.resolved index cd090d7..b8992b5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,25 +1,32 @@ { - "object": { - "pins": [ - { - "package": "Hope", - "repositoryURL": "https://github.com/screensailor/Hope", - "state": { - "branch": "trunk", - "revision": "b76209cf412ba8a78941a298ff0c5d2a58cc4e6a", - "version": null - } - }, - { - "package": "swift-collections", - "repositoryURL": "https://github.com/apple/swift-collections", - "state": { - "branch": null, - "revision": "48254824bb4248676bf7ce56014ff57b142b77eb", - "version": "1.0.2" - } + "pins" : [ + { + "identity" : "hope", + "kind" : "remoteSourceControl", + "location" : "https://github.com/screensailor/Hope", + "state" : { + "branch" : "trunk", + "revision" : "b76209cf412ba8a78941a298ff0c5d2a58cc4e6a" } - ] - }, - "version": 1 + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "f3c9084a71ef4376f2fabbdf1d3d90a49f1fabdb", + "version" : "1.1.2" + } + }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "48254824bb4248676bf7ce56014ff57b142b77eb", + "version" : "1.0.2" + } + } + ], + "version" : 2 } diff --git a/Package.swift b/Package.swift index 20b23d1..ab1b0f6 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.5 +// swift-tools-version: 5.6 import PackageDescription @@ -14,10 +14,13 @@ let package = Package( .library(name: "SwiftStandAlone", targets: ["SwiftStandAlone"]), .library(name: "KotlinStandAlone", targets: ["KotlinStandAlone"]), .library(name: "LexiconGenerators", targets: ["LexiconGenerators"]), + .executable(name: "lexicon-generator", targets: ["lexicon-generator"]), + .plugin(name: "LexiconCodeGeneratorPlugin", targets: ["LexiconCodeGeneratorPlugin"]) ], dependencies: [ - .package(url: "https://github.com/screensailor/Hope", .branch("trunk")), + .package(url: "https://github.com/screensailor/Hope", branch: "trunk"), .package(url: "https://github.com/apple/swift-collections", from: "1.0.0"), + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.2") ], targets: [ @@ -28,7 +31,7 @@ let package = Package( dependencies: [ .product(name: "Collections", package: "swift-collections") ], - swiftSettings: [.define("EDITOR")] // TODO: meke this opt in + swiftSettings: [.define("EDITOR")] // TODO: make this opt in ), .testTarget( name: "LexiconTests", @@ -109,5 +112,28 @@ let package = Package( .copy("Resources"), ] ), + + // MARK: Swift Package Manager Plugin + + .executableTarget( + name: "lexicon-generator", + dependencies: [ + .target(name: "LexiconGenerators"), + .product(name: "ArgumentParser", package: "swift-argument-parser") + ] + ), + .plugin( + name: "LexiconCodeGeneratorPlugin", + capability: .command( + intent: .custom( + verb: "lexicon-generate", + description: "Generate swift code from your lexicon files" + ), + permissions: [ + .writeToPackageDirectory(reason: "This plugin generates code from a lexicon document") + ] + ), + dependencies: ["lexicon-generator"] + ) ] ) diff --git a/Plugins/LexiconCodeGeneratorPlugin/main.swift b/Plugins/LexiconCodeGeneratorPlugin/main.swift new file mode 100644 index 0000000..e216a6f --- /dev/null +++ b/Plugins/LexiconCodeGeneratorPlugin/main.swift @@ -0,0 +1,22 @@ +import PackagePlugin +import Foundation + +@main +struct LexiconCodeGeneratorPlugin: CommandPlugin { + + func performCommand(context: PluginContext, arguments: [String]) throws { + let tool = try context.tool(named: "lexicon-generator") + if let enumerator = FileManager.default.enumerator(atPath: context.package.directory.string) { + for case let path as String in enumerator where path.hasSuffix(".lexicon") || path.hasSuffix(".taskpaper") { + let name = path.replacingOccurrences(of: context.package.directory.string, with: "") + print("\(name)", terminator: " ... ") + let process = Process() + process.executableURL = URL(fileURLWithPath: tool.path.string) + process.arguments = [path, "--type", "swift", "--quiet"] + try process.run() + process.waitUntilExit() + print("✅") + } + } + } +} diff --git a/Sources/KotlinStandAlone/Generator.swift b/Sources/KotlinStandAlone/Generator.swift index 2e2483a..9b882ec 100644 --- a/Sources/KotlinStandAlone/Generator.swift +++ b/Sources/KotlinStandAlone/Generator.swift @@ -9,7 +9,8 @@ public enum Generator: CodeGenerator { // TODO: prefixes? - public static let utType = UTType.sourceCode + public static let utType = UTType(filenameExtension: "kt", conformingTo: .sourceCode)! + public static let command = "kotlin" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { return Data(json.kotlin().utf8) diff --git a/Sources/Lexicon/CodeGenerator.swift b/Sources/Lexicon/CodeGenerator.swift index e754b7c..3d5da6b 100644 --- a/Sources/Lexicon/CodeGenerator.swift +++ b/Sources/Lexicon/CodeGenerator.swift @@ -6,5 +6,6 @@ import UniformTypeIdentifiers public protocol CodeGenerator { static var utType: UTType { get } + static var command: String { get } static func generate(_ json: Lexicon.Graph.JSON) throws -> Data } diff --git a/Sources/Lexicon/JSONClasses.swift b/Sources/Lexicon/JSONClasses.swift index 9dddc41..a8c40bf 100644 --- a/Sources/Lexicon/JSONClasses.swift +++ b/Sources/Lexicon/JSONClasses.swift @@ -8,7 +8,8 @@ import UniformTypeIdentifiers public enum JSONClasses: CodeGenerator { public static let utType: UTType = .json - + public static let command = "json" + public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { let encoder = Encoder() encoder.outputFormatting = [.prettyPrinted, .sortedKeys] diff --git a/Sources/SwiftLexicon/Generator.swift b/Sources/SwiftLexicon/Generator.swift index ff88b4c..d5c6843 100644 --- a/Sources/SwiftLexicon/Generator.swift +++ b/Sources/SwiftLexicon/Generator.swift @@ -10,7 +10,8 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource - + public static let command = "swiftLibrary" + public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { throw "Failed to generate Swift file" diff --git a/Sources/SwiftStandAlone/Generator.swift b/Sources/SwiftStandAlone/Generator.swift index ab3e33d..4b8518a 100644 --- a/Sources/SwiftStandAlone/Generator.swift +++ b/Sources/SwiftStandAlone/Generator.swift @@ -10,7 +10,8 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource - + public static let command = "swift" + public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { throw "Failed to generate Swift file" diff --git a/Sources/lexicon-generator/CodeGenerator.swift b/Sources/lexicon-generator/CodeGenerator.swift new file mode 100644 index 0000000..85b3eec --- /dev/null +++ b/Sources/lexicon-generator/CodeGenerator.swift @@ -0,0 +1,71 @@ +import ArgumentParser +import Collections +import Foundation +import Lexicon +import LexiconGenerators + +@main +struct CodeGeneratorCommand: AsyncParsableCommand { + + @Argument + var input: URL + + @Option(name: .shortAndLong) + var output: URL? + + @Option(name: .shortAndLong) + var type: [String] + + @Flag(name: .shortAndLong) + var quiet: Bool = false + + private var isLogging: Bool { !quiet } + + mutating func run() async throws { + let name = String(input.lastPathComponent.split(separator: ".")[0]) + if isLogging { + print("\(name) lexicon") + } + let output = self.output ?? input.deletingLastPathComponent() + let lexicon = try await Lexicon.from( + TaskPaper(Data(contentsOf: input)).decode() + ) + let json = await lexicon.json() + for commandName in type { + guard + let generator = Lexicon.Graph.JSON.generators.named(commandName), + let `extension` = generator.utType.preferredFilenameExtension + else { continue } + let url = output.appendingPathComponent(name) + .appendingPathExtension(`extension`) + if isLogging { print(url.path) } + try generator.generate(json) + .write(to: url) + } + } +} + +typealias Generators = OrderedDictionary + +extension Generators { + + func named(_ command: String) -> CodeGenerator.Type? { + first { _, value in value.command == command }?.value + } +} + +extension URL: ExpressibleByArgument { + + public init?(argument: String) { + self.init(fileURLWithPath: argument) + } +} + +extension Array: ExpressibleByArgument where Element: ExpressibleByArgument { + + public init?(argument: String) { + self = argument.split(separator: ",").compactMap { substring in + Element(argument: String(substring)) + } + } +} From fd2ab30a02ebed09e32b9de01312aa87ee6445e2 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Sun, 29 May 2022 20:12:18 +0100 Subject: [PATCH 02/14] Rename to lexicon-generate --- Package.swift | 6 +++--- Plugins/LexiconCodeGeneratorPlugin/main.swift | 2 +- .../CodeGenerator.swift | 6 ++++++ 3 files changed, 10 insertions(+), 4 deletions(-) rename Sources/{lexicon-generator => lexicon-generate}/CodeGenerator.swift (90%) diff --git a/Package.swift b/Package.swift index ab1b0f6..ce0efc5 100644 --- a/Package.swift +++ b/Package.swift @@ -14,7 +14,7 @@ let package = Package( .library(name: "SwiftStandAlone", targets: ["SwiftStandAlone"]), .library(name: "KotlinStandAlone", targets: ["KotlinStandAlone"]), .library(name: "LexiconGenerators", targets: ["LexiconGenerators"]), - .executable(name: "lexicon-generator", targets: ["lexicon-generator"]), + .executable(name: "lexicon-generate", targets: ["lexicon-generate"]), .plugin(name: "LexiconCodeGeneratorPlugin", targets: ["LexiconCodeGeneratorPlugin"]) ], dependencies: [ @@ -116,7 +116,7 @@ let package = Package( // MARK: Swift Package Manager Plugin .executableTarget( - name: "lexicon-generator", + name: "lexicon-generate", dependencies: [ .target(name: "LexiconGenerators"), .product(name: "ArgumentParser", package: "swift-argument-parser") @@ -133,7 +133,7 @@ let package = Package( .writeToPackageDirectory(reason: "This plugin generates code from a lexicon document") ] ), - dependencies: ["lexicon-generator"] + dependencies: ["lexicon-generate"] ) ] ) diff --git a/Plugins/LexiconCodeGeneratorPlugin/main.swift b/Plugins/LexiconCodeGeneratorPlugin/main.swift index e216a6f..53d9592 100644 --- a/Plugins/LexiconCodeGeneratorPlugin/main.swift +++ b/Plugins/LexiconCodeGeneratorPlugin/main.swift @@ -5,7 +5,7 @@ import Foundation struct LexiconCodeGeneratorPlugin: CommandPlugin { func performCommand(context: PluginContext, arguments: [String]) throws { - let tool = try context.tool(named: "lexicon-generator") + let tool = try context.tool(named: "lexicon-generate") if let enumerator = FileManager.default.enumerator(atPath: context.package.directory.string) { for case let path as String in enumerator where path.hasSuffix(".lexicon") || path.hasSuffix(".taskpaper") { let name = path.replacingOccurrences(of: context.package.directory.string, with: "") diff --git a/Sources/lexicon-generator/CodeGenerator.swift b/Sources/lexicon-generate/CodeGenerator.swift similarity index 90% rename from Sources/lexicon-generator/CodeGenerator.swift rename to Sources/lexicon-generate/CodeGenerator.swift index 85b3eec..111eacc 100644 --- a/Sources/lexicon-generator/CodeGenerator.swift +++ b/Sources/lexicon-generate/CodeGenerator.swift @@ -7,6 +7,12 @@ import LexiconGenerators @main struct CodeGeneratorCommand: AsyncParsableCommand { + static var configuration = CommandConfiguration( + commandName: "lexicon-generate", + abstract: "A utility for generating code from lexicon documents.", + version: "1.0.0" + ) + @Argument var input: URL From 90b920e9c6af49fa2d1a57d41ca4be688fe6b153 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Sun, 29 May 2022 20:23:22 +0100 Subject: [PATCH 03/14] Rely on utType for generation --- Sources/KotlinStandAlone/Generator.swift | 1 - Sources/Lexicon/CodeGenerator.swift | 1 - Sources/Lexicon/JSONClasses.swift | 1 - Sources/SwiftLexicon/Generator.swift | 1 - Sources/SwiftStandAlone/Generator.swift | 1 - Sources/lexicon-generate/CodeGenerator.swift | 13 +++++-------- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Sources/KotlinStandAlone/Generator.swift b/Sources/KotlinStandAlone/Generator.swift index 9b882ec..3be7d01 100644 --- a/Sources/KotlinStandAlone/Generator.swift +++ b/Sources/KotlinStandAlone/Generator.swift @@ -10,7 +10,6 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType(filenameExtension: "kt", conformingTo: .sourceCode)! - public static let command = "kotlin" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { return Data(json.kotlin().utf8) diff --git a/Sources/Lexicon/CodeGenerator.swift b/Sources/Lexicon/CodeGenerator.swift index 3d5da6b..e754b7c 100644 --- a/Sources/Lexicon/CodeGenerator.swift +++ b/Sources/Lexicon/CodeGenerator.swift @@ -6,6 +6,5 @@ import UniformTypeIdentifiers public protocol CodeGenerator { static var utType: UTType { get } - static var command: String { get } static func generate(_ json: Lexicon.Graph.JSON) throws -> Data } diff --git a/Sources/Lexicon/JSONClasses.swift b/Sources/Lexicon/JSONClasses.swift index a8c40bf..d7f0f99 100644 --- a/Sources/Lexicon/JSONClasses.swift +++ b/Sources/Lexicon/JSONClasses.swift @@ -8,7 +8,6 @@ import UniformTypeIdentifiers public enum JSONClasses: CodeGenerator { public static let utType: UTType = .json - public static let command = "json" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { let encoder = Encoder() diff --git a/Sources/SwiftLexicon/Generator.swift b/Sources/SwiftLexicon/Generator.swift index d5c6843..5ec7290 100644 --- a/Sources/SwiftLexicon/Generator.swift +++ b/Sources/SwiftLexicon/Generator.swift @@ -10,7 +10,6 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource - public static let command = "swiftLibrary" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { diff --git a/Sources/SwiftStandAlone/Generator.swift b/Sources/SwiftStandAlone/Generator.swift index 4b8518a..94a7d2c 100644 --- a/Sources/SwiftStandAlone/Generator.swift +++ b/Sources/SwiftStandAlone/Generator.swift @@ -10,7 +10,6 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource - public static let command = "swift" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { diff --git a/Sources/lexicon-generate/CodeGenerator.swift b/Sources/lexicon-generate/CodeGenerator.swift index 111eacc..401b49c 100644 --- a/Sources/lexicon-generate/CodeGenerator.swift +++ b/Sources/lexicon-generate/CodeGenerator.swift @@ -37,13 +37,10 @@ struct CodeGeneratorCommand: AsyncParsableCommand { TaskPaper(Data(contentsOf: input)).decode() ) let json = await lexicon.json() - for commandName in type { - guard - let generator = Lexicon.Graph.JSON.generators.named(commandName), - let `extension` = generator.utType.preferredFilenameExtension - else { continue } + for fileExtension in type { + guard let generator = Lexicon.Graph.JSON.generators.forExtension(fileExtension) else { continue } let url = output.appendingPathComponent(name) - .appendingPathExtension(`extension`) + .appendingPathExtension(fileExtension) if isLogging { print(url.path) } try generator.generate(json) .write(to: url) @@ -55,8 +52,8 @@ typealias Generators = OrderedDictionary extension Generators { - func named(_ command: String) -> CodeGenerator.Type? { - first { _, value in value.command == command }?.value + func forExtension(_ ext: String) -> CodeGenerator.Type? { + first { _, value in value.utType.preferredFilenameExtension == ext }?.value } } From 967504fa02b3bb0b14ce86558d6359637e7290ab Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Sun, 29 May 2022 20:24:27 +0100 Subject: [PATCH 04/14] Revert "Rely on utType for generation" This reverts commit 90b920e9c6af49fa2d1a57d41ca4be688fe6b153. --- Sources/KotlinStandAlone/Generator.swift | 1 + Sources/Lexicon/CodeGenerator.swift | 1 + Sources/Lexicon/JSONClasses.swift | 1 + Sources/SwiftLexicon/Generator.swift | 1 + Sources/SwiftStandAlone/Generator.swift | 1 + Sources/lexicon-generate/CodeGenerator.swift | 13 ++++++++----- 6 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Sources/KotlinStandAlone/Generator.swift b/Sources/KotlinStandAlone/Generator.swift index 3be7d01..9b882ec 100644 --- a/Sources/KotlinStandAlone/Generator.swift +++ b/Sources/KotlinStandAlone/Generator.swift @@ -10,6 +10,7 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType(filenameExtension: "kt", conformingTo: .sourceCode)! + public static let command = "kotlin" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { return Data(json.kotlin().utf8) diff --git a/Sources/Lexicon/CodeGenerator.swift b/Sources/Lexicon/CodeGenerator.swift index e754b7c..3d5da6b 100644 --- a/Sources/Lexicon/CodeGenerator.swift +++ b/Sources/Lexicon/CodeGenerator.swift @@ -6,5 +6,6 @@ import UniformTypeIdentifiers public protocol CodeGenerator { static var utType: UTType { get } + static var command: String { get } static func generate(_ json: Lexicon.Graph.JSON) throws -> Data } diff --git a/Sources/Lexicon/JSONClasses.swift b/Sources/Lexicon/JSONClasses.swift index d7f0f99..a8c40bf 100644 --- a/Sources/Lexicon/JSONClasses.swift +++ b/Sources/Lexicon/JSONClasses.swift @@ -8,6 +8,7 @@ import UniformTypeIdentifiers public enum JSONClasses: CodeGenerator { public static let utType: UTType = .json + public static let command = "json" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { let encoder = Encoder() diff --git a/Sources/SwiftLexicon/Generator.swift b/Sources/SwiftLexicon/Generator.swift index 5ec7290..d5c6843 100644 --- a/Sources/SwiftLexicon/Generator.swift +++ b/Sources/SwiftLexicon/Generator.swift @@ -10,6 +10,7 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource + public static let command = "swiftLibrary" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { diff --git a/Sources/SwiftStandAlone/Generator.swift b/Sources/SwiftStandAlone/Generator.swift index 94a7d2c..4b8518a 100644 --- a/Sources/SwiftStandAlone/Generator.swift +++ b/Sources/SwiftStandAlone/Generator.swift @@ -10,6 +10,7 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource + public static let command = "swift" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { diff --git a/Sources/lexicon-generate/CodeGenerator.swift b/Sources/lexicon-generate/CodeGenerator.swift index 401b49c..111eacc 100644 --- a/Sources/lexicon-generate/CodeGenerator.swift +++ b/Sources/lexicon-generate/CodeGenerator.swift @@ -37,10 +37,13 @@ struct CodeGeneratorCommand: AsyncParsableCommand { TaskPaper(Data(contentsOf: input)).decode() ) let json = await lexicon.json() - for fileExtension in type { - guard let generator = Lexicon.Graph.JSON.generators.forExtension(fileExtension) else { continue } + for commandName in type { + guard + let generator = Lexicon.Graph.JSON.generators.named(commandName), + let `extension` = generator.utType.preferredFilenameExtension + else { continue } let url = output.appendingPathComponent(name) - .appendingPathExtension(fileExtension) + .appendingPathExtension(`extension`) if isLogging { print(url.path) } try generator.generate(json) .write(to: url) @@ -52,8 +55,8 @@ typealias Generators = OrderedDictionary extension Generators { - func forExtension(_ ext: String) -> CodeGenerator.Type? { - first { _, value in value.utType.preferredFilenameExtension == ext }?.value + func named(_ command: String) -> CodeGenerator.Type? { + first { _, value in value.command == command }?.value } } From e8eef1fcffd934e89853d9793a436e5fddedbadb Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Mon, 30 May 2022 10:12:37 +0100 Subject: [PATCH 05/14] BuildToolPlugin --- Package.swift | 10 +------- Plugins/LexiconCodeGeneratorPlugin/main.swift | 24 +++++++++---------- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/Package.swift b/Package.swift index ce0efc5..acf5fc9 100644 --- a/Package.swift +++ b/Package.swift @@ -124,15 +124,7 @@ let package = Package( ), .plugin( name: "LexiconCodeGeneratorPlugin", - capability: .command( - intent: .custom( - verb: "lexicon-generate", - description: "Generate swift code from your lexicon files" - ), - permissions: [ - .writeToPackageDirectory(reason: "This plugin generates code from a lexicon document") - ] - ), + capability: .buildTool(), dependencies: ["lexicon-generate"] ) ] diff --git a/Plugins/LexiconCodeGeneratorPlugin/main.swift b/Plugins/LexiconCodeGeneratorPlugin/main.swift index 53d9592..e635878 100644 --- a/Plugins/LexiconCodeGeneratorPlugin/main.swift +++ b/Plugins/LexiconCodeGeneratorPlugin/main.swift @@ -2,21 +2,21 @@ import PackagePlugin import Foundation @main -struct LexiconCodeGeneratorPlugin: CommandPlugin { +struct LexiconCodeGeneratorPlugin: BuildToolPlugin { - func performCommand(context: PluginContext, arguments: [String]) throws { - let tool = try context.tool(named: "lexicon-generate") - if let enumerator = FileManager.default.enumerator(atPath: context.package.directory.string) { + func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { + if let enumerator = FileManager.default.enumerator(atPath: target.directory.string) { for case let path as String in enumerator where path.hasSuffix(".lexicon") || path.hasSuffix(".taskpaper") { - let name = path.replacingOccurrences(of: context.package.directory.string, with: "") - print("\(name)", terminator: " ... ") - let process = Process() - process.executableURL = URL(fileURLWithPath: tool.path.string) - process.arguments = [path, "--type", "swift", "--quiet"] - try process.run() - process.waitUntilExit() - print("✅") + return try [ + .prebuildCommand( + displayName: "Generate Swift Lexicon Identifiers", + executable: context.tool(named: "lexicon-generate").path, + arguments: ["--type", "swift", "--quiet"], + outputFilesDirectory: Path(path).removingLastComponent() + ) + ] } } + return [] } } From 1e5ea3e65bdf1cbc558cac80d3e8ff8ba1e6a238 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Mon, 30 May 2022 10:14:30 +0100 Subject: [PATCH 06/14] add missing package dependency --- Package.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index acf5fc9..3303b99 100644 --- a/Package.swift +++ b/Package.swift @@ -119,7 +119,8 @@ let package = Package( name: "lexicon-generate", dependencies: [ .target(name: "LexiconGenerators"), - .product(name: "ArgumentParser", package: "swift-argument-parser") + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Collections", package: "swift-collections") ] ), .plugin( From f9e041e3ce92d23c8611ffb163adf5c06230cf45 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Mon, 30 May 2022 10:21:15 +0100 Subject: [PATCH 07/14] rename main to plugin --- Plugins/LexiconCodeGeneratorPlugin/{main.swift => plugin.swift} | 1 + 1 file changed, 1 insertion(+) rename Plugins/LexiconCodeGeneratorPlugin/{main.swift => plugin.swift} (93%) diff --git a/Plugins/LexiconCodeGeneratorPlugin/main.swift b/Plugins/LexiconCodeGeneratorPlugin/plugin.swift similarity index 93% rename from Plugins/LexiconCodeGeneratorPlugin/main.swift rename to Plugins/LexiconCodeGeneratorPlugin/plugin.swift index e635878..0062d51 100644 --- a/Plugins/LexiconCodeGeneratorPlugin/main.swift +++ b/Plugins/LexiconCodeGeneratorPlugin/plugin.swift @@ -5,6 +5,7 @@ import Foundation struct LexiconCodeGeneratorPlugin: BuildToolPlugin { func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { + print("❌❌❌❌❌❌❌❌❌❌❌❌❌") if let enumerator = FileManager.default.enumerator(atPath: target.directory.string) { for case let path as String in enumerator where path.hasSuffix(".lexicon") || path.hasSuffix(".taskpaper") { return try [ From 052334f7c6d928ce8389aa00a54ebdcf12b55fa8 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Tue, 31 May 2022 10:10:11 +0100 Subject: [PATCH 08/14] Update to buildCommand --- Package.swift | 16 +++++----- .../LexiconCodeGeneratorPlugin/plugin.swift | 32 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Package.swift b/Package.swift index 3303b99..e4b198a 100644 --- a/Package.swift +++ b/Package.swift @@ -115,14 +115,14 @@ let package = Package( // MARK: Swift Package Manager Plugin - .executableTarget( - name: "lexicon-generate", - dependencies: [ - .target(name: "LexiconGenerators"), - .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "Collections", package: "swift-collections") - ] - ), + .executableTarget( + name: "lexicon-generate", + dependencies: [ + .target(name: "LexiconGenerators"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Collections", package: "swift-collections") + ] + ), .plugin( name: "LexiconCodeGeneratorPlugin", capability: .buildTool(), diff --git a/Plugins/LexiconCodeGeneratorPlugin/plugin.swift b/Plugins/LexiconCodeGeneratorPlugin/plugin.swift index 0062d51..30bee1e 100644 --- a/Plugins/LexiconCodeGeneratorPlugin/plugin.swift +++ b/Plugins/LexiconCodeGeneratorPlugin/plugin.swift @@ -5,19 +5,25 @@ import Foundation struct LexiconCodeGeneratorPlugin: BuildToolPlugin { func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { - print("❌❌❌❌❌❌❌❌❌❌❌❌❌") - if let enumerator = FileManager.default.enumerator(atPath: target.directory.string) { - for case let path as String in enumerator where path.hasSuffix(".lexicon") || path.hasSuffix(".taskpaper") { - return try [ - .prebuildCommand( - displayName: "Generate Swift Lexicon Identifiers", - executable: context.tool(named: "lexicon-generate").path, - arguments: ["--type", "swift", "--quiet"], - outputFilesDirectory: Path(path).removingLastComponent() - ) - ] + let lexicon = try context.tool(named: "lexicon-generate") + let output = context.pluginWorkDirectory.appending("GeneratedSources") + return FileManager.default.enumerator(atPath: target.directory.string)? + .compactMap { value in (value as? String).map(target.directory.appending) } + .filter { path in + ["taskpaper", "lexicon"].contains(path.extension) } - } - return [] + .map { input in + return .buildCommand( + displayName: "Generate Swift Lexicon Identifiers for \(input)", + executable: lexicon.path, + arguments: [ + input.string, + "--output", output.string, + "--type", "swift" + ], + inputFiles: [input], + outputFiles: [output.appending("\(input.stem).swift")] + ) + } ?? [] } } From 7769d39df0a53f9fd42ec1bc2750038bc0d03621 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Wed, 1 Jun 2022 20:15:45 +0100 Subject: [PATCH 09/14] Resolve issue with inherited nodes inside of Lemma ownType, add SwiftStandAloneGeneratorPlugin and SwiftLibraryGeneratorPlugin --- Package.swift | 106 +++++++----------- .../SwiftLibraryGeneratorPlugin/plugin.swift | 27 +++++ .../plugin.swift | 10 +- Sources/Lexicon/Lemma.swift | 11 +- .../LexiconTests/Lexicon\342\204\242.swift" | 23 ++++ 5 files changed, 106 insertions(+), 71 deletions(-) create mode 100644 Plugins/SwiftLibraryGeneratorPlugin/plugin.swift rename Plugins/{LexiconCodeGeneratorPlugin => SwiftStandAloneGeneratorPlugin}/plugin.swift (75%) diff --git a/Package.swift b/Package.swift index e4b198a..62601e1 100644 --- a/Package.swift +++ b/Package.swift @@ -15,7 +15,8 @@ let package = Package( .library(name: "KotlinStandAlone", targets: ["KotlinStandAlone"]), .library(name: "LexiconGenerators", targets: ["LexiconGenerators"]), .executable(name: "lexicon-generate", targets: ["lexicon-generate"]), - .plugin(name: "LexiconCodeGeneratorPlugin", targets: ["LexiconCodeGeneratorPlugin"]) + .plugin(name: "SwiftStandAloneGeneratorPlugin", targets: ["SwiftStandAloneGeneratorPlugin"]), + .plugin(name: "SwiftLibraryGeneratorPlugin", targets: ["SwiftLibraryGeneratorPlugin"]), ], dependencies: [ .package(url: "https://github.com/screensailor/Hope", branch: "trunk"), @@ -23,9 +24,6 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser", from: "1.1.2") ], targets: [ - - // MARK: Lexicon - .target( name: "Lexicon", dependencies: [ @@ -39,92 +37,74 @@ let package = Package( "Hope", "Lexicon" ], - resources: [ - .copy("Resources") + resources: [.copy("Resources")] + ), + .target( + name: "LexiconGenerators", + dependencies: [ + "Lexicon", + "SwiftLexicon", + "SwiftStandAlone", + "KotlinStandAlone" + ] + ), + .target( + name: "SwiftLexicon", + dependencies: [ + "Lexicon" ] ), - - // MARK: LexiconGenerators - - .target( - name: "LexiconGenerators", - dependencies: [ - "Lexicon", - "SwiftLexicon", - "SwiftStandAlone", - "KotlinStandAlone" - ] - ), - - // MARK: SwiftLexicon - - .target( - name: "SwiftLexicon", - dependencies: [ - "Lexicon", - ] - ), .testTarget( name: "SwiftLexiconTests", dependencies: [ "Hope", "SwiftLexicon" ], - resources: [ - .copy("Resources"), + resources: [.copy("Resources")] + ), + .target( + name: "SwiftStandAlone", + dependencies: [ + "Lexicon", ] ), - - // MARK: SwiftStandAlone - - .target( - name: "SwiftStandAlone", - dependencies: [ - "Lexicon", - ] - ), .testTarget( name: "SwiftStandAloneTests", dependencies: [ "Hope", "SwiftStandAlone" ], - resources: [ - .copy("Resources"), + resources: [.copy("Resources")] + ), + .target( + name: "KotlinStandAlone", + dependencies: [ + "Lexicon", ] ), - - // MARK: KotlinStandAlones - - .target( - name: "KotlinStandAlone", - dependencies: [ - "Lexicon", - ] - ), .testTarget( name: "KotlinStandAloneTests", dependencies: [ "Hope", "KotlinStandAlone" ], - resources: [ - .copy("Resources"), + resources: [.copy("Resources")] + ), + .executableTarget( + name: "lexicon-generate", + dependencies: [ + .target(name: "LexiconGenerators"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Collections", package: "swift-collections") ] ), - - // MARK: Swift Package Manager Plugin - - .executableTarget( - name: "lexicon-generate", - dependencies: [ - .target(name: "LexiconGenerators"), - .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "Collections", package: "swift-collections") - ] - ), .plugin( - name: "LexiconCodeGeneratorPlugin", + name: "SwiftStandAloneGeneratorPlugin", + capability: .buildTool(), + dependencies: ["lexicon-generate"] + ), + .plugin( + name: "SwiftLibraryGeneratorPlugin", capability: .buildTool(), dependencies: ["lexicon-generate"] ) diff --git a/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift b/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift new file mode 100644 index 0000000..6fb7a5d --- /dev/null +++ b/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift @@ -0,0 +1,27 @@ +import PackagePlugin +import Foundation + +@main +struct SwiftLibraryGeneratorPlugin: BuildToolPlugin { + + func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { + let lexicon = try context.tool(named: "lexicon-generate") + let output = context.pluginWorkDirectory.appending("GeneratedSources") + return FileManager.default.enumerator(atPath: target.directory.string)? + .compactMap { value in (value as? String).map(target.directory.appending) } + .filter { path in (path.extension ?? "").hasSuffix("lexicon") } + .map { input in + .buildCommand( + displayName: "Generate \(input)", + executable: lexicon.path, + arguments: [ + input.string, + "--output", output.string, + "--type", "swiftLibrary" + ], + inputFiles: [input], + outputFiles: [output.appending("\(input.stem).swift")] + ) + } ?? [] + } +} diff --git a/Plugins/LexiconCodeGeneratorPlugin/plugin.swift b/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift similarity index 75% rename from Plugins/LexiconCodeGeneratorPlugin/plugin.swift rename to Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift index 30bee1e..2101fd5 100644 --- a/Plugins/LexiconCodeGeneratorPlugin/plugin.swift +++ b/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift @@ -2,19 +2,17 @@ import PackagePlugin import Foundation @main -struct LexiconCodeGeneratorPlugin: BuildToolPlugin { +struct SwiftStandAloneGeneratorPlugin: BuildToolPlugin { func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { let lexicon = try context.tool(named: "lexicon-generate") let output = context.pluginWorkDirectory.appending("GeneratedSources") return FileManager.default.enumerator(atPath: target.directory.string)? .compactMap { value in (value as? String).map(target.directory.appending) } - .filter { path in - ["taskpaper", "lexicon"].contains(path.extension) - } + .filter { path in (path.extension ?? "").hasSuffix("lexicon") } .map { input in - return .buildCommand( - displayName: "Generate Swift Lexicon Identifiers for \(input)", + .buildCommand( + displayName: "Generate \(input)", executable: lexicon.path, arguments: [ input.string, diff --git a/Sources/Lexicon/Lemma.swift b/Sources/Lexicon/Lemma.swift index e10cf8b..ef809e0 100644 --- a/Sources/Lexicon/Lemma.swift +++ b/Sources/Lexicon/Lemma.swift @@ -358,8 +358,15 @@ extension Lemma { func lazy_ownType() -> [ID: Unowned] { var o: [ID: Unowned] = [:] - for id in node.type { - o[id] = lexicon.dictionary[id].map(Unowned.init) + if isGraphNode { + for id in node.type { + o[id] = lexicon.dictionary[id].map(Unowned.init) + } + } else { + for id in (parent?.node.type).or([]) { + guard let node = lexicon.dictionary[id]?.children[name] else { continue } + o[node.id] = Unowned(node) + } } return o } diff --git "a/Tests/LexiconTests/Lexicon\342\204\242.swift" "b/Tests/LexiconTests/Lexicon\342\204\242.swift" index b743193..c85c3cb 100644 --- "a/Tests/LexiconTests/Lexicon\342\204\242.swift" +++ "b/Tests/LexiconTests/Lexicon\342\204\242.swift" @@ -28,6 +28,20 @@ final class Lexicon™: Hopes { // TODO: ... } + + func test_example() async throws { + + let root = try await Lexicon.from( + TaskPaper(example).decode() + ).root + + let userId = try await root["user", "id"].hopefully() + let collectionId = try await root["db", "collection", "id"].hopefully() + + let isCollectionId = await userId.is(collectionId) + + hope(isCollectionId) == true + } } private let taskpaper = """ @@ -77,3 +91,12 @@ root: json: taskpaper: """ + +private let example = """ +root: + db: + collection: + id: + user: + + root.db.collection +""" From f10a4e17e3b1790a3b53a8dbaa74d7f9294e0d89 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Wed, 1 Jun 2022 20:35:22 +0100 Subject: [PATCH 10/14] More explicit output definition, if provided --- Plugins/SwiftLibraryGeneratorPlugin/plugin.swift | 7 ++++--- Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift | 7 ++++--- Sources/lexicon-generate/CodeGenerator.swift | 13 +++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift b/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift index 6fb7a5d..20e39f4 100644 --- a/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift +++ b/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift @@ -11,16 +11,17 @@ struct SwiftLibraryGeneratorPlugin: BuildToolPlugin { .compactMap { value in (value as? String).map(target.directory.appending) } .filter { path in (path.extension ?? "").hasSuffix("lexicon") } .map { input in - .buildCommand( + let file = output.appending("\(input.stem).swift") + return .buildCommand( displayName: "Generate \(input)", executable: lexicon.path, arguments: [ input.string, - "--output", output.string, + "--output", file.string, "--type", "swiftLibrary" ], inputFiles: [input], - outputFiles: [output.appending("\(input.stem).swift")] + outputFiles: [file] ) } ?? [] } diff --git a/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift b/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift index 2101fd5..69792fa 100644 --- a/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift +++ b/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift @@ -11,16 +11,17 @@ struct SwiftStandAloneGeneratorPlugin: BuildToolPlugin { .compactMap { value in (value as? String).map(target.directory.appending) } .filter { path in (path.extension ?? "").hasSuffix("lexicon") } .map { input in - .buildCommand( + let file = output.appending("\(input.stem).swift") + return .buildCommand( displayName: "Generate \(input)", executable: lexicon.path, arguments: [ input.string, - "--output", output.string, + "--output", file.string, "--type", "swift" ], inputFiles: [input], - outputFiles: [output.appending("\(input.stem).swift")] + outputFiles: [file] ) } ?? [] } diff --git a/Sources/lexicon-generate/CodeGenerator.swift b/Sources/lexicon-generate/CodeGenerator.swift index 111eacc..f33c98a 100644 --- a/Sources/lexicon-generate/CodeGenerator.swift +++ b/Sources/lexicon-generate/CodeGenerator.swift @@ -32,21 +32,22 @@ struct CodeGeneratorCommand: AsyncParsableCommand { if isLogging { print("\(name) lexicon") } - let output = self.output ?? input.deletingLastPathComponent() let lexicon = try await Lexicon.from( TaskPaper(Data(contentsOf: input)).decode() ) let json = await lexicon.json() for commandName in type { guard - let generator = Lexicon.Graph.JSON.generators.named(commandName), + let generator = Lexicon.Graph.JSON.generators.find(commandName), let `extension` = generator.utType.preferredFilenameExtension else { continue } - let url = output.appendingPathComponent(name) + let file = output ?? input + .deletingLastPathComponent() + .appendingPathComponent(name) .appendingPathExtension(`extension`) - if isLogging { print(url.path) } + if isLogging { print(file.path) } try generator.generate(json) - .write(to: url) + .write(to: file) } } } @@ -55,7 +56,7 @@ typealias Generators = OrderedDictionary extension Generators { - func named(_ command: String) -> CodeGenerator.Type? { + func find(_ command: String) -> CodeGenerator.Type? { first { _, value in value.command == command }?.value } } From 88bcf3fdb6dcb063ad1d66137bab73ab8cc03e71 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Wed, 1 Jun 2022 20:37:37 +0100 Subject: [PATCH 11/14] rename test --- "Tests/LexiconTests/Lexicon\342\204\242.swift" | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git "a/Tests/LexiconTests/Lexicon\342\204\242.swift" "b/Tests/LexiconTests/Lexicon\342\204\242.swift" index c85c3cb..f71d414 100644 --- "a/Tests/LexiconTests/Lexicon\342\204\242.swift" +++ "b/Tests/LexiconTests/Lexicon\342\204\242.swift" @@ -29,10 +29,10 @@ final class Lexicon™: Hopes { // TODO: ... } - func test_example() async throws { + func test_inherited_node_own_type() async throws { let root = try await Lexicon.from( - TaskPaper(example).decode() + TaskPaper(inherited_node_own_type).decode() ).root let userId = try await root["user", "id"].hopefully() @@ -92,7 +92,7 @@ root: taskpaper: """ -private let example = """ +private let inherited_node_own_type = """ root: db: collection: From 8b0d730acc1fd1c751e94377ad58b57ad22ca5da Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Thu, 16 Jun 2022 20:59:37 +0100 Subject: [PATCH 12/14] Refinement to names and cli documentation --- .../SwiftLibraryGeneratorPlugin/plugin.swift | 4 +- .../plugin.swift | 4 +- Sources/LexiconGenerators/List.swift | 2 +- Sources/SwiftLexicon/Generator.swift | 4 +- Sources/SwiftStandAlone/Generator.swift | 2 +- Sources/lexicon-generate/CodeGenerator.swift | 57 ++++++++++++++----- 6 files changed, 50 insertions(+), 23 deletions(-) diff --git a/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift b/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift index 20e39f4..a0f4bf6 100644 --- a/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift +++ b/Plugins/SwiftLibraryGeneratorPlugin/plugin.swift @@ -11,14 +11,14 @@ struct SwiftLibraryGeneratorPlugin: BuildToolPlugin { .compactMap { value in (value as? String).map(target.directory.appending) } .filter { path in (path.extension ?? "").hasSuffix("lexicon") } .map { input in - let file = output.appending("\(input.stem).swift") + let file = output.appending(input.stem) return .buildCommand( displayName: "Generate \(input)", executable: lexicon.path, arguments: [ input.string, "--output", file.string, - "--type", "swiftLibrary" + "--type", "swift" ], inputFiles: [input], outputFiles: [file] diff --git a/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift b/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift index 69792fa..35100bd 100644 --- a/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift +++ b/Plugins/SwiftStandAloneGeneratorPlugin/plugin.swift @@ -11,14 +11,14 @@ struct SwiftStandAloneGeneratorPlugin: BuildToolPlugin { .compactMap { value in (value as? String).map(target.directory.appending) } .filter { path in (path.extension ?? "").hasSuffix("lexicon") } .map { input in - let file = output.appending("\(input.stem).swift") + let file = output.appending(input.stem) return .buildCommand( displayName: "Generate \(input)", executable: lexicon.path, arguments: [ input.string, "--output", file.string, - "--type", "swift" + "--type", "swift-standalone" ], inputFiles: [input], outputFiles: [file] diff --git a/Sources/LexiconGenerators/List.swift b/Sources/LexiconGenerators/List.swift index d8eb719..7148ca3 100644 --- a/Sources/LexiconGenerators/List.swift +++ b/Sources/LexiconGenerators/List.swift @@ -12,7 +12,7 @@ public extension Lexicon.Graph.JSON { static let generators: OrderedDictionary = [ - "Swift Library": SwiftLexicon.Generator.self, + "Swift": SwiftLexicon.Generator.self, "Swift Stand-Alone": SwiftStandAlone.Generator.self, diff --git a/Sources/SwiftLexicon/Generator.swift b/Sources/SwiftLexicon/Generator.swift index d5c6843..f2d20c2 100644 --- a/Sources/SwiftLexicon/Generator.swift +++ b/Sources/SwiftLexicon/Generator.swift @@ -10,7 +10,7 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource - public static let command = "swiftLibrary" + public static let command = "swift" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { @@ -25,7 +25,7 @@ private extension Lexicon.Graph.JSON { func swift() -> String { return """ - @_exported import SwiftLexicon // From https://github.com/screensailor/Lexicon + @_exported import SwiftLexicon // https://github.com/thousandyears/Lexicon import Foundation public let \(name) = L_\(name)("\(name)") diff --git a/Sources/SwiftStandAlone/Generator.swift b/Sources/SwiftStandAlone/Generator.swift index 4b8518a..8dce921 100644 --- a/Sources/SwiftStandAlone/Generator.swift +++ b/Sources/SwiftStandAlone/Generator.swift @@ -10,7 +10,7 @@ public enum Generator: CodeGenerator { // TODO: prefixes? public static let utType = UTType.swiftSource - public static let command = "swift" + public static let command = "swift-standalone" public static func generate(_ json: Lexicon.Graph.JSON) throws -> Data { guard let o = json.swift().data(using: .utf8) else { diff --git a/Sources/lexicon-generate/CodeGenerator.swift b/Sources/lexicon-generate/CodeGenerator.swift index f33c98a..90ac6d4 100644 --- a/Sources/lexicon-generate/CodeGenerator.swift +++ b/Sources/lexicon-generate/CodeGenerator.swift @@ -13,13 +13,28 @@ struct CodeGeneratorCommand: AsyncParsableCommand { version: "1.0.0" ) - @Argument + @Argument(help: "URL to the lexicon") var input: URL - @Option(name: .shortAndLong) + @Option( + name: .shortAndLong, + help: "The file path of where the code should be output, if not specified the same directory as the lexicon will be used" + ) var output: URL? - @Option(name: .shortAndLong) + @Option( + name: .shortAndLong, + help: + """ + Types of code to generate from the lexicon. Comma separated values. + + Generators: + \(Lexicon.Graph.JSON.generators.commandHelp) + + Example: + --type swift,kotlin + """ + ) var type: [String] @Flag(name: .shortAndLong) @@ -36,18 +51,24 @@ struct CodeGeneratorCommand: AsyncParsableCommand { TaskPaper(Data(contentsOf: input)).decode() ) let json = await lexicon.json() - for commandName in type { - guard - let generator = Lexicon.Graph.JSON.generators.find(commandName), - let `extension` = generator.utType.preferredFilenameExtension - else { continue } - let file = output ?? input - .deletingLastPathComponent() - .appendingPathComponent(name) - .appendingPathExtension(`extension`) + let code = try type.map { command in + guard let generator = Lexicon.Graph.JSON.generators.find(command) else { + fatalError("Unable to find a generator for \(command)") + } + guard let `extension` = generator.utType.preferredFilenameExtension else { + fatalError("\(command) does not have a valid uniform type identifier: \(generator.utType)") + } + return ( + file: output?.appendingPathExtension(`extension`) + ?? input.deletingLastPathComponent() + .appendingPathComponent(name) + .appendingPathExtension(`extension`), + data: try generator.generate(json) + ) + } + for (file, data) in code { if isLogging { print(file.path) } - try generator.generate(json) - .write(to: file) + try data.write(to: file) } } } @@ -56,6 +77,8 @@ typealias Generators = OrderedDictionary extension Generators { + var commandHelp: String { values.map { $0.command }.joined(separator: ", ") } + func find(_ command: String) -> CodeGenerator.Type? { first { _, value in value.command == command }?.value } @@ -64,7 +87,11 @@ extension Generators { extension URL: ExpressibleByArgument { public init?(argument: String) { - self.init(fileURLWithPath: argument) + if argument.hasPrefix("http") { + self.init(string: argument) + } else { + self.init(fileURLWithPath: argument) + } } } From 799331045eec57fe0419442ecc8bc9eaf2d81143 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Thu, 16 Jun 2022 22:20:46 +0100 Subject: [PATCH 13/14] Remove redundant conformance --- Sources/SwiftLexicon/Event.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/SwiftLexicon/Event.swift b/Sources/SwiftLexicon/Event.swift index 2a86323..94eec31 100644 --- a/Sources/SwiftLexicon/Event.swift +++ b/Sources/SwiftLexicon/Event.swift @@ -52,7 +52,7 @@ public struct Event: @unchecked Sendable, Hashable, Identifiable, CustomStringCo k(\.L) is A } - public func `is`(_ a: K) -> Bool { + public func `is`(_ a: K) -> Bool { k is A && a.____.allSatisfy { k.____[$0] == $1 } From ebbca92ccc5abc712fa5b012ea342f157ff08ac7 Mon Sep 17 00:00:00 2001 From: Oliver Atkinson Date: Fri, 17 Jun 2022 14:39:27 +0100 Subject: [PATCH 14/14] Update CodeGenerator.swift --- Sources/lexicon-generate/CodeGenerator.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/lexicon-generate/CodeGenerator.swift b/Sources/lexicon-generate/CodeGenerator.swift index 90ac6d4..1a38a2d 100644 --- a/Sources/lexicon-generate/CodeGenerator.swift +++ b/Sources/lexicon-generate/CodeGenerator.swift @@ -13,12 +13,12 @@ struct CodeGeneratorCommand: AsyncParsableCommand { version: "1.0.0" ) - @Argument(help: "URL to the lexicon") + @Argument(help: "File path or URL to the lexicon") var input: URL @Option( name: .shortAndLong, - help: "The file path of where the code should be output, if not specified the same directory as the lexicon will be used" + help: "Output path excluding extension, if not specified the same directory and name of the lexicon will be used" ) var output: URL? @@ -26,7 +26,7 @@ struct CodeGeneratorCommand: AsyncParsableCommand { name: .shortAndLong, help: """ - Types of code to generate from the lexicon. Comma separated values. + Types of code to generate. Comma separated. Generators: \(Lexicon.Graph.JSON.generators.commandHelp)