Skip to content

Commit

Permalink
Use swift-argument-parser instead of swift-tools-support-core's vendo…
Browse files Browse the repository at this point in the history
…red parser (#19)

* Use swift-argument-parser instead of swift-tools-support-core's vendored parser

* Require Xcode 11.3 & Swift 5.1
  • Loading branch information
kaandedeoglu authored Feb 29, 2020
1 parent 8395628 commit 3c82718
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 83 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
os: osx
osx_image: xcode11.2
osx_image: xcode11.3
language: swift
sudo: required
script:
Expand Down
10 changes: 5 additions & 5 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@
}
},
{
"package": "swift-tools-support-core",
"repositoryURL": "https://github.com/apple/swift-tools-support-core.git",
"package": "swift-argument-parser",
"repositoryURL": "https://github.com/apple/swift-argument-parser",
"state": {
"branch": "master",
"revision": "f9e7ad4d3dd25d698c53a723ca67cd145418e821",
"version": null
"branch": null,
"revision": "f6ac7b8118ff5d1bc0faee7f37bf6f8fd8f95602",
"version": "0.0.1"
}
},
{
Expand Down
6 changes: 3 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.0
// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand All @@ -10,12 +10,12 @@ let package = Package(
],
dependencies: [
.package(url: "https://github.com/tuist/xcodeproj.git", .upToNextMajor(from: "7.5.0")),
.package(url: "https://github.com/apple/swift-tools-support-core.git", .branch("master"))
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.0.1"),
],
targets: [
.target(
name: "Shark",
dependencies: ["SwiftToolsSupport-auto", "XcodeProj"]),
dependencies: ["XcodeProj", "ArgumentParser"]),
.testTarget(
name: "SharkTests",
dependencies: ["Shark"]),
Expand Down
111 changes: 51 additions & 60 deletions Sources/Shark/Parser.swift
Original file line number Diff line number Diff line change
@@ -1,67 +1,58 @@
import Foundation
import TSCUtility
import ArgumentParser

enum Parser {
struct Result {
let projectPath: String
let outputURL: Foundation.URL
let topLevelEnumName: String
let targetName: String?
let locale: String
}

static func parse() throws -> Result {
let exampleUsageString = "$PROJECT_FILE_PATH $PROJECT_DIR/$PROJECT_NAME"
let parser = ArgumentParser(usage: exampleUsageString, overview: "Shark")
let pathArgument = parser.add(positional: ".xcodeproj path", kind: String.self, usage: "The path to the .xcodeproj file")
let outputArgument = parser.add(positional: "output path", kind: String.self, usage: "Path for the output file. Creates a Shark.swift file when this value is a folder")
let nameArgument = parser.add(option: "--name", kind: String.self, usage: #"Top level enum name under which the cases are defined. Defaults to "Shark""#, completion: nil)
let targetArgument = parser.add(option: "--target", kind: String.self, usage: "Target name of the application, useful in case there are multiple application targets", completion: nil)
let localeArgument = parser.add(option: "--locale",kind: String.self,usage:
#"Localization code to use when selecting the Localizable.strings. i.e "en", "de", "es" The "en" locale is used unless specified"#)

let parseResults: ArgumentParser.Result
do {
parseResults = try parser.parse(Array(CommandLine.arguments.dropFirst()))
} catch {
switch error {
case ArgumentParserError.expectedArguments(_, let missingArguments):
print("Missing arguments: \(missingArguments.joined(separator: ", "))")
print("Example usage: \(exampleUsageString)")
case ArgumentParserError.unknownOption(let option):
print("Unknown option: \(option)")
default:
print(error.localizedDescription)
}
exit(EXIT_FAILURE)
}

guard let projectPath = parseResults.get(pathArgument)?.expandingTildeInPath, let outputPath = parseResults.get(outputArgument)?.expandingTildeInPath else {
print("xcodeproj file path and output path parameters are required")
exit(EXIT_FAILURE)
}

guard projectPath.pathExtension == "xcodeproj" else {
print("\(projectPath) should point to a .xcodeproj file")
exit(EXIT_FAILURE)
struct Options: ParsableArguments {
@Argument(help: "The .xcodeproj file path")
fileprivate(set) var projectPath: String

@Argument(help: "The output file path")
fileprivate(set) var outputPath: String

@Option(name: .customLong("name"),
default: "Shark",
help: "Top level enum name under which the cases are defined.")
private(set) var topLevelEnumName: String

@Option(name: .customLong("target"),
help: "Target name of the application, useful in case there are multiple application targets")
private(set) var targetName: String?

@Option(name: .long,
default: "en",
help: "Localization code to use when selecting the Localizable.strings. i.e en, de, es.")
private(set) var locale: String
}

struct Shark: ParsableCommand {
static var configuration: CommandConfiguration = .init(abstract:#"""
Paste the following line in a Xcode run phase script that runs after the "Compile Sources" run phase:
$PROJECT_FILE_PATH $PROJECT_DIR/$PROJECT_NAME
"""#)

@OptionGroup()
private var options: Options

mutating func validate() throws {
guard options.projectPath.pathExtension == "xcodeproj" else {
throw ValidationError("\(options.projectPath) should point to a .xcodeproj file")
}

var isDirectory: ObjCBool = false

let outputURL: Foundation.URL
if FileManager.default.fileExists(atPath: outputPath, isDirectory: &isDirectory), isDirectory.boolValue {
outputURL = URL(fileURLWithPath: outputPath).appendingPathComponent("Shark.swift")
} else if outputPath.pathExtension == "swift" {
outputURL = URL(fileURLWithPath: outputPath)
} else {
print("The output path should either point to an existing folder or end with a .swift extension")
exit(2)
if FileManager.default.fileExists(atPath: options.outputPath, isDirectory: &isDirectory), isDirectory.boolValue {
options.outputPath.append("Shark.swift")
} else if options.outputPath.pathExtension != "swift" {
throw ValidationError("The output path should either point to an existing folder or end with a .swift extension")
}

return Result(projectPath: projectPath,
outputURL: outputURL,
topLevelEnumName: parseResults.get(nameArgument) ?? "Shark",
targetName: parseResults.get(targetArgument),
locale: parseResults.get(localeArgument) ?? "en")

options.projectPath = options.projectPath.expandingTildeInPath
options.outputPath = options.outputPath.expandingTildeInPath
}

func run() throws {
let enumString = try SharkEnumBuilder.sharkEnumString(forOptions: options)

try FileBuilder
.fileContents(with: enumString, filename: options.outputPath.lastPathComponent)
.write(to: URL(fileURLWithPath: options.outputPath), atomically: true, encoding: .utf8)
}
}
8 changes: 4 additions & 4 deletions Sources/Shark/SharkEnumBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import Foundation

enum SharkEnumBuilder {
static var topLevelEnumName = "Shark"
static func sharkEnumString(forParseResult parseResult: Parser.Result) throws -> String {
SharkEnumBuilder.topLevelEnumName = parseResult.topLevelEnumName
let resourcePaths = try XcodeProjectHelper(parseResult: parseResult).resourcePaths()
static func sharkEnumString(forOptions options: Options) throws -> String {
SharkEnumBuilder.topLevelEnumName = options.topLevelEnumName
let resourcePaths = try XcodeProjectHelper(options: options).resourcePaths()

let imagesString = try ImageEnumBuilder.imageEnumString(forFilesAtPaths: resourcePaths.assetsPaths, topLevelName: "I")
let colorsString = try ColorEnumBuilder.colorEnumString(forFilesAtPaths: resourcePaths.assetsPaths, topLevelName: "C")
Expand All @@ -13,7 +13,7 @@ enum SharkEnumBuilder {
let declarations = [imagesString, colorsString, localizationsString].compactMap({ $0?.indented(withLevel: 1) }).joined(separator: "\n\n")

return """
public enum \(parseResult.topLevelEnumName) {
public enum \(topLevelEnumName) {
private class Custom {}
static var bundle: Bundle { return Bundle(for: Custom.self) }
\(declarations)
Expand Down
8 changes: 4 additions & 4 deletions Sources/Shark/XcodeProjectHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ struct XcodeProjectHelper {
private let targetName: String?
private let locale: String

init(parseResult: Parser.Result) throws {
projectPath = Path(parseResult.projectPath)
init(options: Options) throws {
projectPath = Path(options.projectPath)
xcodeproj = try XcodeProj(path: projectPath)
targetName = parseResult.targetName
locale = parseResult.locale
targetName = options.targetName
locale = options.locale
}

func resourcePaths() throws -> ResourcePaths {
Expand Down
7 changes: 1 addition & 6 deletions Sources/Shark/main.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@
let parseResult = try Parser.parse()
let enumString = try SharkEnumBuilder.sharkEnumString(forParseResult: parseResult)

try FileBuilder
.fileContents(with: enumString, filename: parseResult.outputURL.lastPathComponent)
.write(to: parseResult.outputURL, atomically: true, encoding: .utf8)
Shark.main()

0 comments on commit 3c82718

Please sign in to comment.