diff --git a/.gitignore b/.gitignore index 02c0875..0c62eec 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.build /Packages /*.xcodeproj +.swiftpm diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dfba428 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,5 @@ +FROM swift:4.2.1 +COPY Package.swift ./Package.swift +COPY Sources ./Sources +COPY Tests ./Tests +RUN swift test --configuration debug diff --git a/Makefile b/Makefile deleted file mode 100644 index 3eb263f..0000000 --- a/Makefile +++ /dev/null @@ -1,44 +0,0 @@ -TOOL_NAME = stringray -VERSION = 0.3.0 - -REPO = https://github.com/g-Off/$(TOOL_NAME) -RELEASE_TAR = $(REPO)/archive/$(VERSION).tar.gz -SHA = $(shell curl -L -s $(RELEASE_TAR) | shasum -a 256 | sed 's/ .*//') - -PREFIX = /usr/local -INSTALL_PATH = $(PREFIX)/bin/$(TOOL_NAME) -BUILD_PATH = $(shell swift build --show-bin-path -c $(CONFIGURATION))/$(TOOL_NAME) - -SWIFTC_FLAGS = -Xswiftc "-target" -Xswiftc "x86_64-apple-macosx10.13" -CONFIGURATION = debug - -debug: generate_version -debug: build - -generate_version: - @sed 's/__VERSION__/$(VERSION)/g' Version.swift.template > Sources/stringray/Version.swift - -release: - @echo $(SHA) - -build: - swift build --configuration $(CONFIGURATION) $(SWIFTC_FLAGS) - -install: CONFIGURATION = release -install: SWIFTC_FLAGS += --static-swift-stdlib --disable-sandbox -install: clean build - mkdir -p $(PREFIX)/bin - cp -f $(BUILD_PATH) $(INSTALL_PATH) - -test: - swift test $(SWIFTC_FLAGS) - -xcode: generate_version -xcode: - swift package generate-xcodeproj --xcconfig-overrides=Overrides.xcconfig - xed . - -clean: - swift package clean - -.PHONY: debug release build test xcode clean install diff --git a/Overrides.xcconfig b/Overrides.xcconfig deleted file mode 100644 index 1b57358..0000000 --- a/Overrides.xcconfig +++ /dev/null @@ -1 +0,0 @@ -MACOSX_DEPLOYMENT_TARGET = 10.13 diff --git a/Package.resolved b/Package.resolved index a0130cc..b1e5448 100644 --- a/Package.resolved +++ b/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/scottrhoyt/SwiftyTextTable.git", "state": { "branch": null, - "revision": "7b8661865f0d9590a4b7c146237fecd99f3d8406", - "version": "0.8.2" + "revision": "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version": "0.9.0" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/g-Off/XcodeProject.git", "state": { "branch": null, - "revision": "a1e12a8659684039258b79761c65abb9ec98fc94", - "version": "0.4.0" + "revision": "f5095a860de4cd1f0e635957bed7c4b80392dac8", + "version": "0.5.0" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/jpsim/Yams.git", "state": { "branch": null, - "revision": "26ab35f50ea891e8edefcc9d975db2f6b67e1d68", - "version": "1.0.1" + "revision": "b08dba4bcea978bf1ad37703a384097d3efce5af", + "version": "1.0.2" } } ] diff --git a/Package.swift b/Package.swift index 76b4f38..ad65b3b 100644 --- a/Package.swift +++ b/Package.swift @@ -1,8 +1,11 @@ -// swift-tools-version:4.2 +// swift-tools-version:5.0 import PackageDescription let package = Package( name: "stringray", + platforms: [ + .macOS(.v10_14) + ], products: [ .executable( name: "stringray", @@ -16,8 +19,8 @@ let package = Package( dependencies: [ .package(url: "https://github.com/jpsim/Yams.git", from: "1.0.1"), .package(url: "https://github.com/scottrhoyt/SwiftyTextTable.git", from: "0.5.0"), - .package(url: "https://github.com/g-Off/XcodeProject.git", from: "0.4.0"), - .package(url: "https://github.com/g-Off/CommandRegistry.git", .branch("master")), + .package(url: "https://github.com/g-Off/XcodeProject.git", from: "0.5.0-alpha.3"), + .package(url: "https://github.com/g-Off/CommandRegistry.git", from: "0.1.0"), .package(url: "https://github.com/apple/swift-package-manager.git", from: "0.3.0") ], targets: [ @@ -26,15 +29,15 @@ let package = Package( dependencies: [ "CommandRegistry", "RayGun", - "SwiftyTextTable" + "SwiftyTextTable", + "XcodeProject", + "Utility", + "Yams", ] ), .target( name: "RayGun", dependencies: [ - "Utility", - "Yams", - "XcodeProject" ] ), .testTarget( diff --git a/Sources/RayGun/Exports.swift b/Sources/RayGun/Exports.swift deleted file mode 100644 index 812f4b4..0000000 --- a/Sources/RayGun/Exports.swift +++ /dev/null @@ -1,11 +0,0 @@ -// -// Exports.swift -// RayGun -// -// Created by Geoffrey Foster on 2019-02-01. -// - -@_exported import Basic -@_exported import SwiftyTextTable -@_exported import Utility -@_exported import XcodeProject diff --git a/Sources/RayGun/Lint Rules/Linter.swift b/Sources/RayGun/Lint Rules/Linter.swift index 24779dc..b9bbfb2 100644 --- a/Sources/RayGun/Lint Rules/Linter.swift +++ b/Sources/RayGun/Lint Rules/Linter.swift @@ -6,7 +6,6 @@ // import Foundation -import Yams public struct Linter { public struct Config: Decodable { @@ -29,11 +28,6 @@ public struct Linter { self.rules = [:] } - public init(url: Foundation.URL) throws { - let string = try String(contentsOf: url, encoding: .utf8) - self = try YAMLDecoder().decode(Config.self, from: string, userInfo: [:]) - } - public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.included = try container.decodeIfPresent([String].self, forKey: .included) ?? [] @@ -47,7 +41,8 @@ public struct Linter { public static let allRules: [LintRule] = [ MissingLocalizationLintRule(), OrphanedLocalizationLintRule(), - MissingPlaceholderLintRule() + MissingPlaceholderLintRule(), + MissingCommentLintRule() ] public struct Error: LocalizedError { diff --git a/Sources/RayGun/Lint Rules/MissingCommentLintRule.swift b/Sources/RayGun/Lint Rules/MissingCommentLintRule.swift new file mode 100644 index 0000000..d4f4402 --- /dev/null +++ b/Sources/RayGun/Lint Rules/MissingCommentLintRule.swift @@ -0,0 +1,25 @@ +// +// MissingCommentLintRule.swift +// RayGun +// +// Created by Geoffrey Foster on 2019-06-02. +// + +import Foundation + +struct MissingCommentLintRule: LintRule { + let info: RuleInfo = RuleInfo(identifier: "missing_comment", name: "Missing Comment", description: "", severity: .error) + + func scan(table: StringsTable, url: Foundation.URL, config: Linter.Config.Rule?) throws -> [LintRuleViolation] { + var violations: [LintRuleViolation] = [] + let file = Foundation.URL(fileURLWithPath: "\(table.base.identifier).lproj/\(table.name).strings", relativeTo: url) + for entry in table.baseEntries where entry.comment == nil { + let line = entry.location?.line + let location = LintRuleViolation.Location(file: file, line: line) + let reason = "Mismatched placeholders \(entry.key)" + let violation = LintRuleViolation(locale: table.base, location: location, severity: config?.severity ?? info.severity, reason: reason) + violations.append(violation) + } + return violations + } +} diff --git a/Sources/RayGun/Strings Table/StringsTable.swift b/Sources/RayGun/Strings Table/StringsTable.swift index b86c348..dabaabc 100644 --- a/Sources/RayGun/Strings Table/StringsTable.swift +++ b/Sources/RayGun/Strings Table/StringsTable.swift @@ -45,7 +45,7 @@ public struct StringsTable: Codable { return dictEntries[base] ?? [:] } - public init(name: String, base: Locale, entries: EntriesType, dictEntries: DictEntriesType) { + public init(name: String, base: Locale, entries: EntriesType = [:], dictEntries: DictEntriesType = [:]) { self.name = name self.base = base self.entries = entries @@ -125,7 +125,7 @@ public struct StringsTable: Codable { } private mutating func replace(entry: Entry, with otherEntry: Entry, locale: Locale) { - guard let index = entries[locale]?.index(of: entry) else { return } + guard let index = entries[locale]?.firstIndex(of: entry) else { return } entries[locale]?[index] = otherEntry } diff --git a/Sources/RayGun/Utilities/OrderedSet.swift b/Sources/RayGun/Utilities/OrderedSet.swift index 93beb1e..188c97c 100644 --- a/Sources/RayGun/Utilities/OrderedSet.swift +++ b/Sources/RayGun/Utilities/OrderedSet.swift @@ -58,7 +58,7 @@ extension OrderedSet: SetAlgebra { @discardableResult public mutating func update(with newMember: Element) -> Element? { - if contains(newMember), let index = orderedStorage.index(of: newMember) { + if contains(newMember), let index = orderedStorage.firstIndex(of: newMember) { orderedStorage[index] = newMember } let result = storage.update(with: newMember) @@ -70,7 +70,7 @@ extension OrderedSet: SetAlgebra { @discardableResult public mutating func remove(_ member: Element) -> Element? { - guard let index = orderedStorage.index(of: member) else { return nil } + guard let index = orderedStorage.firstIndex(of: member) else { return nil } orderedStorage.remove(at: index) storage.remove(member) return member diff --git a/Sources/stringray/Commands/CopyCommand.swift b/Sources/stringray/Commands/CopyCommand.swift index 1b6ed1b..5d71d5c 100644 --- a/Sources/stringray/Commands/CopyCommand.swift +++ b/Sources/stringray/Commands/CopyCommand.swift @@ -6,8 +6,10 @@ // import Foundation -import RayGun import CommandRegistry +import Basic +import Utility +import RayGun struct CopyCommand: Command { private struct Arguments { @@ -17,17 +19,17 @@ struct CopyCommand: Command { } let command: String = "copy" let overview: String = "Copy keys matching the given pattern from one strings table to another." - + private let binder: ArgumentBinder - + init(parser: ArgumentParser) { binder = ArgumentBinder() let subparser = parser.add(subparser: command, overview: overview) - + let inputFile = subparser.add(positional: "inputFile", kind: PathArgument.self, optional: false, usage: "", completion: .filename) let outputFile = subparser.add(positional: "outputFile", kind: PathArgument.self, optional: false, usage: "", completion: .filename) let prefix = subparser.add(option: "--prefix", shortName: "-p", kind: [String].self, strategy: .oneByOne, usage: "", completion: nil) - + binder.bind(positional: inputFile) { (arguments, inputFile) in arguments.inputFile = URL(fileURLWithPath: inputFile.path.asString) } @@ -40,18 +42,18 @@ struct CopyCommand: Command { } } } - + func run(with arguments: ArgumentParser.Result) throws { var commandArgs = Arguments() try binder.fill(parseResult: arguments, into: &commandArgs) try copy(from: commandArgs.inputFile, to: commandArgs.outputFile, matching: commandArgs.matching) } - + private func copy(from: Foundation.URL, to: Foundation.URL, matching: [Match]) throws { let loader = StringsTableLoader() let fromTable = try loader.load(url: from) var toTable = try loader.load(url: to) - + let filteredTable = fromTable.withKeys(matching: matching) toTable.addEntries(from: filteredTable) try loader.write(to: to.resourceDirectory, table: toTable) diff --git a/Sources/stringray/Commands/LintCommand.swift b/Sources/stringray/Commands/LintCommand.swift index 60c9359..a68eefb 100644 --- a/Sources/stringray/Commands/LintCommand.swift +++ b/Sources/stringray/Commands/LintCommand.swift @@ -6,8 +6,12 @@ // import Foundation -import RayGun import CommandRegistry +import Basic +import Utility +import RayGun +import XcodeProject +import SwiftyTextTable struct LintCommand: Command { private struct Arguments { diff --git a/Sources/stringray/Commands/MoveCommand.swift b/Sources/stringray/Commands/MoveCommand.swift index c92f945..766140f 100644 --- a/Sources/stringray/Commands/MoveCommand.swift +++ b/Sources/stringray/Commands/MoveCommand.swift @@ -7,6 +7,8 @@ import Foundation import CommandRegistry +import Basic +import Utility import RayGun struct MoveCommand: Command { diff --git a/Sources/stringray/Commands/RenameCommand.swift b/Sources/stringray/Commands/RenameCommand.swift index 04dac71..ae02496 100644 --- a/Sources/stringray/Commands/RenameCommand.swift +++ b/Sources/stringray/Commands/RenameCommand.swift @@ -6,8 +6,10 @@ // import Foundation -import RayGun import CommandRegistry +import Basic +import Utility +import RayGun struct RenameCommand: Command { private struct Arguments { diff --git a/Sources/stringray/Commands/SortCommand.swift b/Sources/stringray/Commands/SortCommand.swift index ad08ec9..e448fab 100644 --- a/Sources/stringray/Commands/SortCommand.swift +++ b/Sources/stringray/Commands/SortCommand.swift @@ -8,6 +8,8 @@ import Foundation import RayGun import CommandRegistry +import Basic +import Utility struct SortCommand: Command { private struct Arguments { @@ -15,25 +17,25 @@ struct SortCommand: Command { } let command: String = "sort" let overview: String = "Sorts the given strings table alphabetically by key." - + private let binder: ArgumentBinder - + init(parser: ArgumentParser) { binder = ArgumentBinder() let subparser = parser.add(subparser: command, overview: overview) let inputFile = subparser.add(positional: "inputFile", kind: PathArgument.self, optional: false, usage: "", completion: .filename) - + binder.bind(positional: inputFile) { (arguments, inputFile) in arguments.inputFile = URL(fileURLWithPath: inputFile.path.asString) } } - + func run(with arguments: ArgumentParser.Result) throws { var commandArgs = Arguments() try binder.fill(parseResult: arguments, into: &commandArgs) try sort(url: commandArgs.inputFile) } - + private func sort(url: Foundation.URL) throws { let loader = StringsTableLoader() var table = try loader.load(url: url) diff --git a/Sources/stringray/Version.swift b/Sources/stringray/Version.swift index 1e5e950..1781604 100644 --- a/Sources/stringray/Version.swift +++ b/Sources/stringray/Version.swift @@ -1,5 +1,5 @@ import Utility extension Version { - static var current: Version = "0.3.0" + static var current: Version = "0.3.1" } diff --git a/Sources/stringray/main.swift b/Sources/stringray/main.swift index f393736..9833a2b 100644 --- a/Sources/stringray/main.swift +++ b/Sources/stringray/main.swift @@ -9,6 +9,15 @@ import Foundation import Utility import CommandRegistry +import Yams +import RayGun + +extension Linter.Config { + public init(url: Foundation.URL) throws { + let string = try String(contentsOf: url, encoding: .utf8) + self = try YAMLDecoder().decode(Linter.Config.self, from: string, userInfo: [:]) + } +} var registry = Registry(usage: " ", overview: "", version: Version.current) registry.register(command: MoveCommand.self) diff --git a/Tests/stringrayTests/LinterTests.swift b/Tests/stringrayTests/LinterTests.swift new file mode 100644 index 0000000..6b33472 --- /dev/null +++ b/Tests/stringrayTests/LinterTests.swift @@ -0,0 +1,36 @@ +// +// LinterTests.swift +// stringrayTests +// +// Created by Geoffrey Foster on 2019-02-01. +// + +import XCTest +@testable import RayGun + +class LinterTests: XCTestCase { + struct TestReporter: Reporter { + func generateReport(for violations: [LintRuleViolation], to outputStream: inout Target) where Target : TextOutputStream { + + } + } + func testLint() { + var entries = StringsTable.EntriesType() + let baseLocale = Locale(identifier: "en") + let otherLocale = Locale(identifier: "fr") + let key = "key" + entries[baseLocale, default: OrderedSet()].append( + StringsTable.Entry(location: nil, comment: nil, key: key, value: "%1$@ mentioned you on %2$@ at %3$@") + ) + entries[otherLocale, default: OrderedSet()].append( + StringsTable.Entry(location: nil, comment: nil, key: key, value: "%1$@ mentioné youé oné %1$@ até %1$@") + ) + let table = StringsTable(name: "Test", base: baseLocale, entries: entries) + let linter = Linter(reporter: TestReporter()) + do { + try linter.report(on: table, url: URL(fileURLWithPath: "file://does/not/exist")) + } catch { + print("hello") + } + } +} diff --git a/Version.swift.template b/Version.swift.template deleted file mode 100644 index a6e9e7b..0000000 --- a/Version.swift.template +++ /dev/null @@ -1,5 +0,0 @@ -import Utility - -extension Version { - static var current: Version = "__VERSION__" -}