From 59968c28e5df858b90ef85a607b3de289b64cc2a Mon Sep 17 00:00:00 2001 From: Pere Bohigas Date: Sun, 12 May 2024 04:29:29 +0200 Subject: [PATCH] Add ArgumentParser (#16) * Add ArgumentParser dependency * Rename SwiftPolyglotCore struct * Add RuntimeError * Rename SwiftPolyglot struct, add ParsableCommand adoption and add RuntimeError usage * Parse arguments and flag using ArgumentParser and adjust SwiftPolyglotCore initializer's parameters * Update README * Rename property * Format * Fix command name in help's message --- Package.resolved | 14 +++++++ Package.swift | 8 +++- README.md | 8 ++-- Sources/SwiftPolyglot/RuntimeError.swift | 15 +++++++ Sources/SwiftPolyglot/SwiftPolyglot.swift | 36 ++++++++++++++++ Sources/SwiftPolyglot/main.swift | 22 ---------- ...Polyglot.swift => SwiftPolyglotCore.swift} | 42 +++++++------------ .../SwiftPolyglotError.swift | 11 ++--- .../SwiftPolyglotCoreTests.swift | 36 +++++++++------- 9 files changed, 116 insertions(+), 76 deletions(-) create mode 100644 Package.resolved create mode 100644 Sources/SwiftPolyglot/RuntimeError.swift create mode 100755 Sources/SwiftPolyglot/SwiftPolyglot.swift delete mode 100755 Sources/SwiftPolyglot/main.swift rename Sources/SwiftPolyglotCore/{SwiftPolyglot.swift => SwiftPolyglotCore.swift} (87%) diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..d126c77 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "46989693916f56d1186bd59ac15124caef896560", + "version" : "1.3.1" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index 8178487..394e2ee 100644 --- a/Package.swift +++ b/Package.swift @@ -8,10 +8,16 @@ let package = Package( products: [ .executable(name: "swiftpolyglot", targets: ["SwiftPolyglot"]), ], + dependencies: [ + .package(url: "https://github.com/apple/swift-argument-parser.git", .upToNextMajor(from: "1.3.1")), + ], targets: [ .executableTarget( name: "SwiftPolyglot", - dependencies: ["SwiftPolyglotCore"] + dependencies: [ + "SwiftPolyglotCore", + .product(name: "ArgumentParser", package: "swift-argument-parser") + ] ), .target(name: "SwiftPolyglotCore"), .testTarget( diff --git a/README.md b/README.md index c423e9d..191d9c8 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,14 @@ $ swift build -c release ``` $ cd ../path/to/your/project -$ swift run --package-path ../path/to/SwiftPolyglot swiftpolyglot "en,es,de" +$ swift run --package-path ../path/to/SwiftPolyglot swiftpolyglot en es de ``` ## Arguments -You must specify at least one language code, they must be within quotation marks, and they must be separated by commas. If you are not providing a translation for your language of origin, you do not need to specify that language. Otherwise, you will get errors due to missing translations. +You must specify at least one language code, and they must be separated by spaces. If you are not providing a translation for your language of origin, you do not need to specify that language. Otherwise, you will get errors due to missing translations. -By default, SwiftPolyglot will not throw an error at the end of the script if there are translations missing. However, you can enable error throwing by adding the argument `--errorOnMissing` +By default, SwiftPolyglot will not throw an error at the end of the script if there are translations missing. However, you can enable error throwing by adding the flag `--error-on-missing` ## Integrating with GitHub Actions @@ -60,6 +60,6 @@ jobs: - name: validate translations run: | swift build --package-path ../SwiftPolyglot --configuration release - swift run --package-path ../SwiftPolyglot swiftpolyglot "es,fr,de,it" --errorOnMissing + swift run --package-path ../SwiftPolyglot swiftpolyglot es fr de it --error-on-missing ``` diff --git a/Sources/SwiftPolyglot/RuntimeError.swift b/Sources/SwiftPolyglot/RuntimeError.swift new file mode 100644 index 0000000..1b9c919 --- /dev/null +++ b/Sources/SwiftPolyglot/RuntimeError.swift @@ -0,0 +1,15 @@ +enum RuntimeError: Error { + case coreError(description: String) + case fileListingNotPossible +} + +extension RuntimeError: CustomStringConvertible { + var description: String { + switch self { + case let .coreError(description): + return description + case .fileListingNotPossible: + return "It was not possible to list all files to be checked" + } + } +} diff --git a/Sources/SwiftPolyglot/SwiftPolyglot.swift b/Sources/SwiftPolyglot/SwiftPolyglot.swift new file mode 100755 index 0000000..bc50d52 --- /dev/null +++ b/Sources/SwiftPolyglot/SwiftPolyglot.swift @@ -0,0 +1,36 @@ +import ArgumentParser +import Foundation +import SwiftPolyglotCore + +@main +struct SwiftPolyglot: ParsableCommand { + static let configuration: CommandConfiguration = .init(commandName: "swiftpolyglot") + + @Flag(help: "Log errors instead of warnings for missing translations.") + private var errorOnMissing = false + + @Argument(help: "Specify the language(s) to be checked.") + private var languages: [String] + + func run() throws { + guard + let enumerator = FileManager.default.enumerator(atPath: FileManager.default.currentDirectoryPath), + let filePaths = enumerator.allObjects as? [String] + else { + throw RuntimeError.fileListingNotPossible + } + + let swiftPolyglotCore: SwiftPolyglotCore = .init( + filePaths: filePaths, + languageCodes: languages, + logsErrorOnMissingTranslation: errorOnMissing, + isRunningInAGitHubAction: ProcessInfo.processInfo.environment["GITHUB_ACTIONS"] == "true" + ) + + do { + try swiftPolyglotCore.run() + } catch { + throw RuntimeError.coreError(description: error.localizedDescription) + } + } +} diff --git a/Sources/SwiftPolyglot/main.swift b/Sources/SwiftPolyglot/main.swift deleted file mode 100755 index 7bc4e28..0000000 --- a/Sources/SwiftPolyglot/main.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation -import SwiftPolyglotCore - -guard - let enumerator = FileManager.default.enumerator(atPath: FileManager.default.currentDirectoryPath), - let filePaths = enumerator.allObjects as? [String] -else { - exit(EXIT_FAILURE) -} - -do { - let swiftPolyglot: SwiftPolyglot = try .init( - arguments: Array(CommandLine.arguments.dropFirst()), - filePaths: filePaths, - runningOnAGitHubAction: ProcessInfo.processInfo.environment["GITHUB_ACTIONS"] == "true" - ) - - try swiftPolyglot.run() -} catch { - print(error.localizedDescription) - exit(EXIT_FAILURE) -} diff --git a/Sources/SwiftPolyglotCore/SwiftPolyglot.swift b/Sources/SwiftPolyglotCore/SwiftPolyglotCore.swift similarity index 87% rename from Sources/SwiftPolyglotCore/SwiftPolyglot.swift rename to Sources/SwiftPolyglotCore/SwiftPolyglotCore.swift index dd2e9d4..79a5bdd 100644 --- a/Sources/SwiftPolyglotCore/SwiftPolyglot.swift +++ b/Sources/SwiftPolyglotCore/SwiftPolyglotCore.swift @@ -1,31 +1,21 @@ import Foundation -public struct SwiftPolyglot { - private static let errorOnMissingArgument = "--errorOnMissing" - - private let arguments: [String] +public struct SwiftPolyglotCore { private let filePaths: [String] private let languageCodes: [String] - private let runningOnAGitHubAction: Bool - - private var logErrorOnMissing: Bool { - arguments.contains(Self.errorOnMissingArgument) - } - - public init(arguments: [String], filePaths: [String], runningOnAGitHubAction: Bool) throws { - let languageCodes = arguments[0].split(separator: ",").map(String.init) - - guard - !languageCodes.contains(Self.errorOnMissingArgument), - !languageCodes.isEmpty - else { - throw SwiftPolyglotError.noLanguageCodes - } - - self.arguments = arguments + private let logsErrorOnMissingTranslation: Bool + private let isRunningInAGitHubAction: Bool + + public init( + filePaths: [String], + languageCodes: [String], + logsErrorOnMissingTranslation: Bool, + isRunningInAGitHubAction: Bool + ) { self.filePaths = filePaths self.languageCodes = languageCodes - self.runningOnAGitHubAction = runningOnAGitHubAction + self.logsErrorOnMissingTranslation = logsErrorOnMissingTranslation + self.isRunningInAGitHubAction = isRunningInAGitHubAction } public func run() throws { @@ -33,7 +23,7 @@ public struct SwiftPolyglot { try searchDirectory(for: languageCodes, missingTranslations: &missingTranslations) - if missingTranslations, logErrorOnMissing { + if missingTranslations, logsErrorOnMissingTranslation { throw SwiftPolyglotError.missingTranslations } else if missingTranslations { print("Completed with missing translations.") @@ -90,7 +80,7 @@ public struct SwiftPolyglot { let jsonDict = jsonObject as? [String: Any], let strings = jsonDict["strings"] as? [String: [String: Any]] else { - if runningOnAGitHubAction { + if isRunningInAGitHubAction { print("::warning file=\(fileURL.path)::Could not process file at path: \(fileURL.path)") } else { print("Could not process file at path: \(fileURL.path)") @@ -170,8 +160,8 @@ public struct SwiftPolyglot { } private func logWarning(file: String, message: String) { - if runningOnAGitHubAction { - if logErrorOnMissing { + if isRunningInAGitHubAction { + if logsErrorOnMissingTranslation { print("::error file=\(file)::\(message)") } else { print("::warning file=\(file)::\(message)") diff --git a/Sources/SwiftPolyglotCore/SwiftPolyglotError.swift b/Sources/SwiftPolyglotCore/SwiftPolyglotError.swift index c4113ac..5fb6601 100644 --- a/Sources/SwiftPolyglotCore/SwiftPolyglotError.swift +++ b/Sources/SwiftPolyglotCore/SwiftPolyglotError.swift @@ -2,19 +2,16 @@ import Foundation enum SwiftPolyglotError: Error { case missingTranslations - case noLanguageCodes case unsupportedVariation(variation: String) } extension SwiftPolyglotError: LocalizedError { public var errorDescription: String? { switch self { - case .missingTranslations: - return "Error: One or more translations are missing." - case .noLanguageCodes: - return "Usage: swiftpolyglot [--errorOnMissing]" - case let .unsupportedVariation(variation): - return "Variation type '\(variation)' is not supported. Please create an issue in GitHub" + case .missingTranslations: + return "Error: One or more translations are missing." + case let .unsupportedVariation(variation): + return "Variation type '\(variation)' is not supported. Please create an issue in GitHub" } } } diff --git a/Tests/SwiftPolyglotCoreTests/SwiftPolyglotCoreTests.swift b/Tests/SwiftPolyglotCoreTests/SwiftPolyglotCoreTests.swift index 5f7f21c..ab86650 100644 --- a/Tests/SwiftPolyglotCoreTests/SwiftPolyglotCoreTests.swift +++ b/Tests/SwiftPolyglotCoreTests/SwiftPolyglotCoreTests.swift @@ -14,13 +14,14 @@ final class SwiftPolyglotCoreTests: XCTestCase { return } - let swiftPolyglot: SwiftPolyglot = try .init( - arguments: ["ca,de,en,es"], + let swiftPolyglotCore: SwiftPolyglotCore = .init( filePaths: [stringCatalogFilePath], - runningOnAGitHubAction: false + languageCodes: ["ca", "de", "en", "es"], + logsErrorOnMissingTranslation: false, + isRunningInAGitHubAction: false ) - XCTAssertNoThrow(try swiftPolyglot.run()) + XCTAssertNoThrow(try swiftPolyglotCore.run()) } func testStringCatalogVariationsFullyTranslated() throws { @@ -35,13 +36,14 @@ final class SwiftPolyglotCoreTests: XCTestCase { return } - let swiftPolyglot: SwiftPolyglot = try .init( - arguments: ["ca,de,en,es"], + let swiftPolyglotCore: SwiftPolyglotCore = .init( filePaths: [stringCatalogFilePath], - runningOnAGitHubAction: false + languageCodes: ["ca", "de", "en", "es"], + logsErrorOnMissingTranslation: false, + isRunningInAGitHubAction: false ) - XCTAssertNoThrow(try swiftPolyglot.run()) + XCTAssertNoThrow(try swiftPolyglotCore.run()) } func testStringCatalogWithMissingTranslations() throws { @@ -56,13 +58,14 @@ final class SwiftPolyglotCoreTests: XCTestCase { return } - let swiftPolyglot: SwiftPolyglot = try .init( - arguments: ["ca,de,en,es", "--errorOnMissing"], + let swiftPolyglotCore: SwiftPolyglotCore = .init( filePaths: [stringCatalogFilePath], - runningOnAGitHubAction: false + languageCodes: ["ca", "de", "en", "es"], + logsErrorOnMissingTranslation: true, + isRunningInAGitHubAction: false ) - XCTAssertThrowsError(try swiftPolyglot.run()) + XCTAssertThrowsError(try swiftPolyglotCore.run()) } func testStringCatalogWithMissingVariations() throws { @@ -77,12 +80,13 @@ final class SwiftPolyglotCoreTests: XCTestCase { return } - let swiftPolyglot: SwiftPolyglot = try .init( - arguments: ["de,en", "--errorOnMissing"], + let swiftPolyglotCore: SwiftPolyglotCore = .init( filePaths: [stringCatalogFilePath], - runningOnAGitHubAction: false + languageCodes: ["de, en"], + logsErrorOnMissingTranslation: true, + isRunningInAGitHubAction: false ) - XCTAssertThrowsError(try swiftPolyglot.run()) + XCTAssertThrowsError(try swiftPolyglotCore.run()) } }