mirror of
https://github.com/mas-cli/mas
synced 2025-02-16 20:48:30 +00:00
🔥 Deintegrate CocoaSeeds
This commit is contained in:
parent
97f56a39fa
commit
89c9ee68c9
17 changed files with 0 additions and 1827 deletions
7
Seedfile
7
Seedfile
|
@ -1,7 +0,0 @@
|
||||||
github "carthage/Commandant", "0.13.0", files: "Sources/Commandant/*.swift"
|
|
||||||
github "antitypical/Result", "3.2.4", files: "Result/*.swift"
|
|
||||||
|
|
||||||
target :"mas-tests" do
|
|
||||||
github "Quick/Nimble", "v7.0.3", files: "Nimble/**.{swift,h}"
|
|
||||||
github "Quick/Quick", "v1.2.0", files: "Quick/**.{swift,h}"
|
|
||||||
end
|
|
|
@ -1,99 +0,0 @@
|
||||||
//
|
|
||||||
// Argument.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Syo Ikeda on 12/14/15.
|
|
||||||
// Copyright (c) 2015 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
/// Describes an argument that can be provided on the command line.
|
|
||||||
public struct Argument<T> {
|
|
||||||
/// The default value for this argument. This is the value that will be used
|
|
||||||
/// if the argument is never explicitly specified on the command line.
|
|
||||||
///
|
|
||||||
/// If this is nil, this argument is always required.
|
|
||||||
public let defaultValue: T?
|
|
||||||
|
|
||||||
/// A human-readable string describing the purpose of this argument. This will
|
|
||||||
/// be shown in help messages.
|
|
||||||
public let usage: String
|
|
||||||
|
|
||||||
public init(defaultValue: T? = nil, usage: String) {
|
|
||||||
self.defaultValue = defaultValue
|
|
||||||
self.usage = usage
|
|
||||||
}
|
|
||||||
|
|
||||||
fileprivate func invalidUsageError<ClientError>(_ value: String) -> CommandantError<ClientError> {
|
|
||||||
let description = "Invalid value for '\(self)': \(value)"
|
|
||||||
return .usageError(description: description)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Operators
|
|
||||||
|
|
||||||
extension CommandMode {
|
|
||||||
/// Evaluates the given argument in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, the argument's `defaultValue` is used.
|
|
||||||
public static func <| <T: ArgumentProtocol, ClientError>(mode: CommandMode, argument: Argument<T>) -> Result<T, CommandantError<ClientError>> {
|
|
||||||
switch mode {
|
|
||||||
case let .arguments(arguments):
|
|
||||||
guard let stringValue = arguments.consumePositionalArgument() else {
|
|
||||||
if let defaultValue = argument.defaultValue {
|
|
||||||
return .success(defaultValue)
|
|
||||||
} else {
|
|
||||||
return .failure(missingArgumentError(argument.usage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let value = T.from(string: stringValue) {
|
|
||||||
return .success(value)
|
|
||||||
} else {
|
|
||||||
return .failure(argument.invalidUsageError(stringValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
case .usage:
|
|
||||||
return .failure(informativeUsageError(argument))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given argument list in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, the argument's `defaultValue` is used.
|
|
||||||
public static func <| <T: ArgumentProtocol, ClientError>(mode: CommandMode, argument: Argument<[T]>) -> Result<[T], CommandantError<ClientError>> {
|
|
||||||
switch mode {
|
|
||||||
case let .arguments(arguments):
|
|
||||||
guard let firstValue = arguments.consumePositionalArgument() else {
|
|
||||||
if let defaultValue = argument.defaultValue {
|
|
||||||
return .success(defaultValue)
|
|
||||||
} else {
|
|
||||||
return .failure(missingArgumentError(argument.usage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var values = [T]()
|
|
||||||
|
|
||||||
guard let value = T.from(string: firstValue) else {
|
|
||||||
return .failure(argument.invalidUsageError(firstValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
values.append(value)
|
|
||||||
|
|
||||||
while let nextValue = arguments.consumePositionalArgument() {
|
|
||||||
guard let value = T.from(string: nextValue) else {
|
|
||||||
return .failure(argument.invalidUsageError(nextValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
values.append(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return .success(values)
|
|
||||||
|
|
||||||
case .usage:
|
|
||||||
return .failure(informativeUsageError(argument))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,191 +0,0 @@
|
||||||
//
|
|
||||||
// ArgumentParser.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Justin Spahr-Summers on 2014-11-21.
|
|
||||||
// Copyright (c) 2014 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Represents an argument passed on the command line.
|
|
||||||
private enum RawArgument: Equatable {
|
|
||||||
/// A key corresponding to an option (e.g., `verbose` for `--verbose`).
|
|
||||||
case key(String)
|
|
||||||
|
|
||||||
/// A value, either associated with an option or passed as a positional
|
|
||||||
/// argument.
|
|
||||||
case value(String)
|
|
||||||
|
|
||||||
/// One or more flag arguments (e.g 'r' and 'f' for `-rf`)
|
|
||||||
case flag(OrderedSet<Character>)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func ==(lhs: RawArgument, rhs: RawArgument) -> Bool {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case let (.key(left), .key(right)):
|
|
||||||
return left == right
|
|
||||||
|
|
||||||
case let (.value(left), .value(right)):
|
|
||||||
return left == right
|
|
||||||
|
|
||||||
case let (.flag(left), .flag(right)):
|
|
||||||
return left == right
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RawArgument: CustomStringConvertible {
|
|
||||||
fileprivate var description: String {
|
|
||||||
switch self {
|
|
||||||
case let .key(key):
|
|
||||||
return "--\(key)"
|
|
||||||
|
|
||||||
case let .value(value):
|
|
||||||
return "\"\(value)\""
|
|
||||||
|
|
||||||
case let .flag(flags):
|
|
||||||
return "-\(String(flags))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Destructively parses a list of command-line arguments.
|
|
||||||
public final class ArgumentParser {
|
|
||||||
/// The remaining arguments to be extracted, in their raw form.
|
|
||||||
private var rawArguments: [RawArgument] = []
|
|
||||||
|
|
||||||
/// Initializes the generator from a simple list of command-line arguments.
|
|
||||||
public init(_ arguments: [String]) {
|
|
||||||
// The first instance of `--` terminates the option list.
|
|
||||||
let params = arguments.split(maxSplits: 1, omittingEmptySubsequences: false) { $0 == "--" }
|
|
||||||
|
|
||||||
// Parse out the keyed and flag options.
|
|
||||||
let options = params.first!
|
|
||||||
rawArguments.append(contentsOf: options.map { arg in
|
|
||||||
if arg.hasPrefix("-") {
|
|
||||||
// Do we have `--{key}` or `-{flags}`.
|
|
||||||
let opt = arg.dropFirst()
|
|
||||||
if opt.first == "-" {
|
|
||||||
return .key(String(opt.dropFirst()))
|
|
||||||
} else {
|
|
||||||
return .flag(OrderedSet(opt))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return .value(arg)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Remaining arguments are all positional parameters.
|
|
||||||
if params.count == 2 {
|
|
||||||
let positional = params.last!
|
|
||||||
rawArguments.append(contentsOf: positional.map(RawArgument.value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the remaining arguments.
|
|
||||||
internal var remainingArguments: [String]? {
|
|
||||||
return rawArguments.isEmpty ? nil : rawArguments.map { $0.description }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the given key was enabled or disabled, or nil if it
|
|
||||||
/// was not given at all.
|
|
||||||
///
|
|
||||||
/// If the key is found, it is then removed from the list of arguments
|
|
||||||
/// remaining to be parsed.
|
|
||||||
internal func consumeBoolean(forKey key: String) -> Bool? {
|
|
||||||
let oldArguments = rawArguments
|
|
||||||
rawArguments.removeAll()
|
|
||||||
|
|
||||||
var result: Bool?
|
|
||||||
for arg in oldArguments {
|
|
||||||
if arg == .key(key) {
|
|
||||||
result = true
|
|
||||||
} else if arg == .key("no-\(key)") {
|
|
||||||
result = false
|
|
||||||
} else {
|
|
||||||
rawArguments.append(arg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the value associated with the given flag, or nil if the flag was
|
|
||||||
/// not specified. If the key is presented, but no value was given, an error
|
|
||||||
/// is returned.
|
|
||||||
///
|
|
||||||
/// If a value is found, the key and the value are both removed from the
|
|
||||||
/// list of arguments remaining to be parsed.
|
|
||||||
internal func consumeValue(forKey key: String) -> Result<String?, CommandantError<NoError>> {
|
|
||||||
let oldArguments = rawArguments
|
|
||||||
rawArguments.removeAll()
|
|
||||||
|
|
||||||
var foundValue: String?
|
|
||||||
var index = 0
|
|
||||||
|
|
||||||
while index < oldArguments.count {
|
|
||||||
defer { index += 1 }
|
|
||||||
let arg = oldArguments[index]
|
|
||||||
|
|
||||||
guard arg == .key(key) else {
|
|
||||||
rawArguments.append(arg)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
index += 1
|
|
||||||
guard index < oldArguments.count, case let .value(value) = oldArguments[index] else {
|
|
||||||
return .failure(missingArgumentError("--\(key)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
foundValue = value
|
|
||||||
}
|
|
||||||
|
|
||||||
return .success(foundValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next positional argument that hasn't yet been returned, or
|
|
||||||
/// nil if there are no more positional arguments.
|
|
||||||
internal func consumePositionalArgument() -> String? {
|
|
||||||
for (index, arg) in rawArguments.enumerated() {
|
|
||||||
if case let .value(value) = arg {
|
|
||||||
rawArguments.remove(at: index)
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the given key was specified and removes it from the
|
|
||||||
/// list of arguments remaining.
|
|
||||||
internal func consume(key: String) -> Bool {
|
|
||||||
let oldArguments = rawArguments
|
|
||||||
rawArguments = oldArguments.filter { $0 != .key(key) }
|
|
||||||
|
|
||||||
return rawArguments.count < oldArguments.count
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether the given flag was specified and removes it from the
|
|
||||||
/// list of arguments remaining.
|
|
||||||
internal func consumeBoolean(flag: Character) -> Bool {
|
|
||||||
for (index, arg) in rawArguments.enumerated() {
|
|
||||||
if case let .flag(flags) = arg, flags.contains(flag) {
|
|
||||||
var flags = flags
|
|
||||||
flags.remove(flag)
|
|
||||||
|
|
||||||
if flags.isEmpty {
|
|
||||||
rawArguments.remove(at: index)
|
|
||||||
} else {
|
|
||||||
rawArguments[index] = .flag(flags)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
//
|
|
||||||
// ArgumentProtocol.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Syo Ikeda on 12/14/15.
|
|
||||||
// Copyright (c) 2015 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
/// Represents a value that can be converted from a command-line argument.
|
|
||||||
public protocol ArgumentProtocol {
|
|
||||||
/// A human-readable name for this type.
|
|
||||||
static var name: String { get }
|
|
||||||
|
|
||||||
/// Attempts to parse a value from the given command-line argument.
|
|
||||||
static func from(string: String) -> Self?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Int: ArgumentProtocol {
|
|
||||||
public static let name = "integer"
|
|
||||||
|
|
||||||
public static func from(string: String) -> Int? {
|
|
||||||
return Int(string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension String: ArgumentProtocol {
|
|
||||||
public static let name = "string"
|
|
||||||
|
|
||||||
public static func from(string: String) -> String? {
|
|
||||||
return string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RawRepresentable where RawValue: StringProtocol, Self: ArgumentProtocol {
|
|
||||||
public static func from(string: String) -> Self? {
|
|
||||||
return RawValue(string).flatMap(Self.init(rawValue:))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RawRepresentable where RawValue: FixedWidthInteger, Self: ArgumentProtocol {
|
|
||||||
public static func from(string: String) -> Self? {
|
|
||||||
return RawValue(string).flatMap(Self.init(rawValue:))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,222 +0,0 @@
|
||||||
//
|
|
||||||
// Command.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Justin Spahr-Summers on 2014-10-10.
|
|
||||||
// Copyright (c) 2014 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Represents a subcommand that can be executed with its own set of arguments.
|
|
||||||
public protocol CommandProtocol {
|
|
||||||
|
|
||||||
/// The command's options type.
|
|
||||||
associatedtype Options: OptionsProtocol
|
|
||||||
|
|
||||||
associatedtype ClientError: Error = Options.ClientError
|
|
||||||
|
|
||||||
/// The action that users should specify to use this subcommand (e.g.,
|
|
||||||
/// `help`).
|
|
||||||
var verb: String { get }
|
|
||||||
|
|
||||||
/// A human-readable, high-level description of what this command is used
|
|
||||||
/// for.
|
|
||||||
var function: String { get }
|
|
||||||
|
|
||||||
/// Runs this subcommand with the given options.
|
|
||||||
func run(_ options: Options) -> Result<(), ClientError>
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type-erased command.
|
|
||||||
public struct CommandWrapper<ClientError: Error> {
|
|
||||||
public let verb: String
|
|
||||||
public let function: String
|
|
||||||
|
|
||||||
public let run: (ArgumentParser) -> Result<(), CommandantError<ClientError>>
|
|
||||||
|
|
||||||
public let usage: () -> CommandantError<ClientError>?
|
|
||||||
|
|
||||||
/// Creates a command that wraps another.
|
|
||||||
fileprivate init<C: CommandProtocol>(_ command: C) where C.ClientError == ClientError, C.Options.ClientError == ClientError {
|
|
||||||
verb = command.verb
|
|
||||||
function = command.function
|
|
||||||
run = { (arguments: ArgumentParser) -> Result<(), CommandantError<ClientError>> in
|
|
||||||
let options = C.Options.evaluate(.arguments(arguments))
|
|
||||||
|
|
||||||
if let remainingArguments = arguments.remainingArguments {
|
|
||||||
return .failure(unrecognizedArgumentsError(remainingArguments))
|
|
||||||
}
|
|
||||||
|
|
||||||
switch options {
|
|
||||||
case let .success(options):
|
|
||||||
return command
|
|
||||||
.run(options)
|
|
||||||
.mapError(CommandantError.commandError)
|
|
||||||
|
|
||||||
case let .failure(error):
|
|
||||||
return .failure(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usage = { () -> CommandantError<ClientError>? in
|
|
||||||
return C.Options.evaluate(.usage).error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes the "mode" in which a command should run.
|
|
||||||
public enum CommandMode {
|
|
||||||
/// Options should be parsed from the given command-line arguments.
|
|
||||||
case arguments(ArgumentParser)
|
|
||||||
|
|
||||||
/// Each option should record its usage information in an error, for
|
|
||||||
/// presentation to the user.
|
|
||||||
case usage
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maintains the list of commands available to run.
|
|
||||||
public final class CommandRegistry<ClientError: Error> {
|
|
||||||
private var commandsByVerb: [String: CommandWrapper<ClientError>] = [:]
|
|
||||||
|
|
||||||
/// All available commands.
|
|
||||||
public var commands: [CommandWrapper<ClientError>] {
|
|
||||||
return commandsByVerb.values.sorted { return $0.verb < $1.verb }
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
/// Registers the given commands, making those available to run.
|
|
||||||
///
|
|
||||||
/// If another commands were already registered with the same `verb`s, those
|
|
||||||
/// will be overwritten.
|
|
||||||
@discardableResult
|
|
||||||
public func register<C: CommandProtocol>(_ commands: C...)
|
|
||||||
-> CommandRegistry
|
|
||||||
where C.ClientError == ClientError, C.Options.ClientError == ClientError
|
|
||||||
{
|
|
||||||
for command in commands {
|
|
||||||
commandsByVerb[command.verb] = CommandWrapper(command)
|
|
||||||
}
|
|
||||||
return self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runs the command corresponding to the given verb, passing it the given
|
|
||||||
/// arguments.
|
|
||||||
///
|
|
||||||
/// Returns the results of the execution, or nil if no such command exists.
|
|
||||||
public func run(command verb: String, arguments: [String]) -> Result<(), CommandantError<ClientError>>? {
|
|
||||||
return self[verb]?.run(ArgumentParser(arguments))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the command matching the given verb, or nil if no such command
|
|
||||||
/// is registered.
|
|
||||||
public subscript(verb: String) -> CommandWrapper<ClientError>? {
|
|
||||||
return commandsByVerb[verb]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CommandRegistry {
|
|
||||||
/// Hands off execution to the CommandRegistry, by parsing CommandLine.arguments
|
|
||||||
/// and then running whichever command has been identified in the argument
|
|
||||||
/// list.
|
|
||||||
///
|
|
||||||
/// If the chosen command executes successfully, the process will exit with
|
|
||||||
/// a successful exit code.
|
|
||||||
///
|
|
||||||
/// If the chosen command fails, the provided error handler will be invoked,
|
|
||||||
/// then the process will exit with a failure exit code.
|
|
||||||
///
|
|
||||||
/// If a matching command could not be found but there is any `executable-verb`
|
|
||||||
/// style subcommand executable in the caller's `$PATH`, the subcommand will
|
|
||||||
/// be executed.
|
|
||||||
///
|
|
||||||
/// If a matching command could not be found or a usage error occurred,
|
|
||||||
/// a helpful error message will be written to `stderr`, then the process
|
|
||||||
/// will exit with a failure error code.
|
|
||||||
public func main(defaultVerb: String, errorHandler: (ClientError) -> ()) -> Never {
|
|
||||||
main(arguments: CommandLine.arguments, defaultVerb: defaultVerb, errorHandler: errorHandler)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hands off execution to the CommandRegistry, by parsing `arguments`
|
|
||||||
/// and then running whichever command has been identified in the argument
|
|
||||||
/// list.
|
|
||||||
///
|
|
||||||
/// If the chosen command executes successfully, the process will exit with
|
|
||||||
/// a successful exit code.
|
|
||||||
///
|
|
||||||
/// If the chosen command fails, the provided error handler will be invoked,
|
|
||||||
/// then the process will exit with a failure exit code.
|
|
||||||
///
|
|
||||||
/// If a matching command could not be found but there is any `executable-verb`
|
|
||||||
/// style subcommand executable in the caller's `$PATH`, the subcommand will
|
|
||||||
/// be executed.
|
|
||||||
///
|
|
||||||
/// If a matching command could not be found or a usage error occurred,
|
|
||||||
/// a helpful error message will be written to `stderr`, then the process
|
|
||||||
/// will exit with a failure error code.
|
|
||||||
public func main(arguments: [String], defaultVerb: String, errorHandler: (ClientError) -> ()) -> Never {
|
|
||||||
assert(arguments.count >= 1)
|
|
||||||
|
|
||||||
var arguments = arguments
|
|
||||||
|
|
||||||
// Extract the executable name.
|
|
||||||
let executableName = arguments.remove(at: 0)
|
|
||||||
|
|
||||||
// use the default verb even if we have other arguments
|
|
||||||
var verb = defaultVerb
|
|
||||||
if let argument = arguments.first, !argument.hasPrefix("-") {
|
|
||||||
verb = argument
|
|
||||||
// Remove the command name.
|
|
||||||
arguments.remove(at: 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch run(command: verb, arguments: arguments) {
|
|
||||||
case .success?:
|
|
||||||
exit(EXIT_SUCCESS)
|
|
||||||
|
|
||||||
case let .failure(error)?:
|
|
||||||
switch error {
|
|
||||||
case let .usageError(description):
|
|
||||||
fputs(description + "\n", stderr)
|
|
||||||
|
|
||||||
case let .commandError(error):
|
|
||||||
errorHandler(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(EXIT_FAILURE)
|
|
||||||
|
|
||||||
case nil:
|
|
||||||
if let subcommandExecuted = executeSubcommandIfExists(executableName, verb: verb, arguments: arguments) {
|
|
||||||
exit(subcommandExecuted)
|
|
||||||
}
|
|
||||||
|
|
||||||
fputs("Unrecognized command: '\(verb)'. See `\(executableName) help`.\n", stderr)
|
|
||||||
exit(EXIT_FAILURE)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Finds and executes a subcommand which exists in your $PATH. The executable
|
|
||||||
/// name must be in the form of `executable-verb`.
|
|
||||||
///
|
|
||||||
/// - Returns: The exit status of found subcommand or nil.
|
|
||||||
private func executeSubcommandIfExists(_ executableName: String, verb: String, arguments: [String]) -> Int32? {
|
|
||||||
let subcommand = "\(NSString(string: executableName).lastPathComponent)-\(verb)"
|
|
||||||
|
|
||||||
func launchTask(_ path: String, arguments: [String]) -> Int32 {
|
|
||||||
let task = Process()
|
|
||||||
task.launchPath = path
|
|
||||||
task.arguments = arguments
|
|
||||||
|
|
||||||
task.launch()
|
|
||||||
task.waitUntilExit()
|
|
||||||
|
|
||||||
return task.terminationStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
guard launchTask("/usr/bin/which", arguments: [ "-s", subcommand ]) == 0 else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return launchTask("/usr/bin/env", arguments: [ subcommand ] + arguments)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
//
|
|
||||||
// Commandant.h
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Justin Spahr-Summers on 2014-11-21.
|
|
||||||
// Copyright (c) 2014 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
//! Project version number for Commandant.
|
|
||||||
FOUNDATION_EXPORT double CommandantVersionNumber;
|
|
||||||
|
|
||||||
//! Project version string for Commandant.
|
|
||||||
FOUNDATION_EXPORT const unsigned char CommandantVersionString[];
|
|
||||||
|
|
||||||
// In this header, you should import all the public headers of your framework using statements like #import <Commandant/PublicHeader.h>
|
|
|
@ -1,153 +0,0 @@
|
||||||
//
|
|
||||||
// Errors.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Justin Spahr-Summers on 2014-10-24.
|
|
||||||
// Copyright (c) 2014 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Possible errors that can originate from Commandant.
|
|
||||||
///
|
|
||||||
/// `ClientError` should be the type of error (if any) that can occur when
|
|
||||||
/// running commands.
|
|
||||||
public enum CommandantError<ClientError>: Error {
|
|
||||||
/// An option was used incorrectly.
|
|
||||||
case usageError(description: String)
|
|
||||||
|
|
||||||
/// An error occurred while running a command.
|
|
||||||
case commandError(ClientError)
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CommandantError: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
switch self {
|
|
||||||
case let .usageError(description):
|
|
||||||
return description
|
|
||||||
|
|
||||||
case let .commandError(error):
|
|
||||||
return String(describing: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an `InvalidArgument` error that indicates a missing value for
|
|
||||||
/// the argument by the given name.
|
|
||||||
internal func missingArgumentError<ClientError>(_ argumentName: String) -> CommandantError<ClientError> {
|
|
||||||
let description = "Missing argument for \(argumentName)"
|
|
||||||
return .usageError(description: description)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error by combining the example of key (and value, if applicable)
|
|
||||||
/// with the usage description.
|
|
||||||
internal func informativeUsageError<ClientError>(_ keyValueExample: String, usage: String) -> CommandantError<ClientError> {
|
|
||||||
let lines = usage.components(separatedBy: .newlines)
|
|
||||||
|
|
||||||
return .usageError(description: lines.reduce(keyValueExample) { previous, value in
|
|
||||||
return previous + "\n\t" + value
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Combines the text of the two errors, if they're both `UsageError`s.
|
|
||||||
/// Otherwise, uses whichever one is not (biased toward the left).
|
|
||||||
internal func combineUsageErrors<ClientError>(_ lhs: CommandantError<ClientError>, _ rhs: CommandantError<ClientError>) -> CommandantError<ClientError> {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case let (.usageError(left), .usageError(right)):
|
|
||||||
let combinedDescription = "\(left)\n\n\(right)"
|
|
||||||
return .usageError(description: combinedDescription)
|
|
||||||
|
|
||||||
case (.usageError, _):
|
|
||||||
return rhs
|
|
||||||
|
|
||||||
case (_, .usageError), (_, _):
|
|
||||||
return lhs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that indicates unrecognized arguments remains.
|
|
||||||
internal func unrecognizedArgumentsError<ClientError>(_ options: [String]) -> CommandantError<ClientError> {
|
|
||||||
return .usageError(description: "Unrecognized arguments: " + options.joined(separator: ", "))
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Argument
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the argument, with the given
|
|
||||||
/// example of value usage if applicable.
|
|
||||||
internal func informativeUsageError<T, ClientError>(_ valueExample: String, argument: Argument<T>) -> CommandantError<ClientError> {
|
|
||||||
if argument.defaultValue != nil {
|
|
||||||
return informativeUsageError("[\(valueExample)]", usage: argument.usage)
|
|
||||||
} else {
|
|
||||||
return informativeUsageError(valueExample, usage: argument.usage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the argument.
|
|
||||||
internal func informativeUsageError<T: ArgumentProtocol, ClientError>(_ argument: Argument<T>) -> CommandantError<ClientError> {
|
|
||||||
var example = ""
|
|
||||||
|
|
||||||
var valueExample = ""
|
|
||||||
if let defaultValue = argument.defaultValue {
|
|
||||||
valueExample = "\(defaultValue)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if valueExample.isEmpty {
|
|
||||||
example += "(\(T.name))"
|
|
||||||
} else {
|
|
||||||
example += valueExample
|
|
||||||
}
|
|
||||||
|
|
||||||
return informativeUsageError(example, argument: argument)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the argument list.
|
|
||||||
internal func informativeUsageError<T: ArgumentProtocol, ClientError>(_ argument: Argument<[T]>) -> CommandantError<ClientError> {
|
|
||||||
var example = ""
|
|
||||||
|
|
||||||
var valueExample = ""
|
|
||||||
if let defaultValue = argument.defaultValue {
|
|
||||||
valueExample = "\(defaultValue)"
|
|
||||||
}
|
|
||||||
|
|
||||||
if valueExample.isEmpty {
|
|
||||||
example += "(\(T.name))"
|
|
||||||
} else {
|
|
||||||
example += valueExample
|
|
||||||
}
|
|
||||||
|
|
||||||
return informativeUsageError(example, argument: argument)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Option
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the option, with the given
|
|
||||||
/// example of key (and value, if applicable) usage.
|
|
||||||
internal func informativeUsageError<T, ClientError>(_ keyValueExample: String, option: Option<T>) -> CommandantError<ClientError> {
|
|
||||||
return informativeUsageError("[\(keyValueExample)]", usage: option.usage)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the option.
|
|
||||||
internal func informativeUsageError<T: ArgumentProtocol, ClientError>(_ option: Option<T>) -> CommandantError<ClientError> {
|
|
||||||
return informativeUsageError("--\(option.key) \(option.defaultValue)", option: option)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the option.
|
|
||||||
internal func informativeUsageError<T: ArgumentProtocol, ClientError>(_ option: Option<T?>) -> CommandantError<ClientError> {
|
|
||||||
return informativeUsageError("--\(option.key) (\(T.name))", option: option)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the option.
|
|
||||||
internal func informativeUsageError<T: ArgumentProtocol, ClientError>(_ option: Option<[T]>) -> CommandantError<ClientError> {
|
|
||||||
return informativeUsageError("--\(option.key) (\(option.defaultValue))", option: option)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the option.
|
|
||||||
internal func informativeUsageError<T: ArgumentProtocol, ClientError>(_ option: Option<[T]?>) -> CommandantError<ClientError> {
|
|
||||||
return informativeUsageError("--\(option.key) (\(T.name))", option: option)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs an error that describes how to use the given boolean option.
|
|
||||||
internal func informativeUsageError<ClientError>(_ option: Option<Bool>) -> CommandantError<ClientError> {
|
|
||||||
let key = option.key
|
|
||||||
return informativeUsageError((option.defaultValue ? "--no-\(key)" : "--\(key)"), option: option)
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
//
|
|
||||||
// HelpCommand.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Justin Spahr-Summers on 2014-10-10.
|
|
||||||
// Copyright (c) 2014 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// A basic implementation of a `help` command, using information available in a
|
|
||||||
/// `CommandRegistry`.
|
|
||||||
///
|
|
||||||
/// If you want to use this command, initialize it with the registry, then add
|
|
||||||
/// it to that same registry:
|
|
||||||
///
|
|
||||||
/// let commands: CommandRegistry<MyErrorType> = …
|
|
||||||
/// let helpCommand = HelpCommand(registry: commands)
|
|
||||||
/// commands.register(helpCommand)
|
|
||||||
public struct HelpCommand<ClientError: Error>: CommandProtocol {
|
|
||||||
public typealias Options = HelpOptions<ClientError>
|
|
||||||
|
|
||||||
public let verb = "help"
|
|
||||||
public let function = "Display general or command-specific help"
|
|
||||||
|
|
||||||
private let registry: CommandRegistry<ClientError>
|
|
||||||
|
|
||||||
/// Initializes the command to provide help from the given registry of
|
|
||||||
/// commands.
|
|
||||||
public init(registry: CommandRegistry<ClientError>) {
|
|
||||||
self.registry = registry
|
|
||||||
}
|
|
||||||
|
|
||||||
public func run(_ options: Options) -> Result<(), ClientError> {
|
|
||||||
if let verb = options.verb {
|
|
||||||
if let command = self.registry[verb] {
|
|
||||||
print(command.function)
|
|
||||||
if let usageError = command.usage() {
|
|
||||||
print("\n\(usageError)")
|
|
||||||
}
|
|
||||||
return .success(())
|
|
||||||
} else {
|
|
||||||
fputs("Unrecognized command: '\(verb)'\n", stderr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print("Available commands:\n")
|
|
||||||
|
|
||||||
let maxVerbLength = self.registry.commands.map { $0.verb.count }.max() ?? 0
|
|
||||||
|
|
||||||
for command in self.registry.commands {
|
|
||||||
let padding = repeatElement(Character(" "), count: maxVerbLength - command.verb.count)
|
|
||||||
print(" \(command.verb)\(String(padding)) \(command.function)")
|
|
||||||
}
|
|
||||||
|
|
||||||
return .success(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct HelpOptions<ClientError: Error>: OptionsProtocol {
|
|
||||||
fileprivate let verb: String?
|
|
||||||
|
|
||||||
private init(verb: String?) {
|
|
||||||
self.verb = verb
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func create(_ verb: String) -> HelpOptions {
|
|
||||||
return self.init(verb: (verb == "" ? nil : verb))
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func evaluate(_ m: CommandMode) -> Result<HelpOptions, CommandantError<ClientError>> {
|
|
||||||
return create
|
|
||||||
<*> m <| Argument(defaultValue: "", usage: "the command to display help for")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>FMWK</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>0.13.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
|
||||||
<key>NSHumanReadableCopyright</key>
|
|
||||||
<string>Copyright © 2014 Carthage. All rights reserved.</string>
|
|
||||||
<key>NSPrincipalClass</key>
|
|
||||||
<string></string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
|
@ -1,273 +0,0 @@
|
||||||
//
|
|
||||||
// Option.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Justin Spahr-Summers on 2014-11-21.
|
|
||||||
// Copyright (c) 2014 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Represents a record of options for a command, which can be parsed from
|
|
||||||
/// a list of command-line arguments.
|
|
||||||
///
|
|
||||||
/// This is most helpful when used in conjunction with the `Option` and `Switch`
|
|
||||||
/// types, and `<*>` and `<|` combinators.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
///
|
|
||||||
/// struct LogOptions: OptionsProtocol {
|
|
||||||
/// let verbosity: Int
|
|
||||||
/// let outputFilename: String
|
|
||||||
/// let shouldDelete: Bool
|
|
||||||
/// let logName: String
|
|
||||||
///
|
|
||||||
/// static func create(_ verbosity: Int) -> (String) -> (Bool) -> (String) -> LogOptions {
|
|
||||||
/// return { outputFilename in { shouldDelete in { logName in LogOptions(verbosity: verbosity, outputFilename: outputFilename, shouldDelete: shouldDelete, logName: logName) } } }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// static func evaluate(_ m: CommandMode) -> Result<LogOptions, CommandantError<YourErrorType>> {
|
|
||||||
/// return create
|
|
||||||
/// <*> m <| Option(key: "verbose", defaultValue: 0, usage: "the verbosity level with which to read the logs")
|
|
||||||
/// <*> m <| Option(key: "outputFilename", defaultValue: "", usage: "a file to print output to, instead of stdout")
|
|
||||||
/// <*> m <| Switch(flag: "d", key: "delete", usage: "delete the logs when finished")
|
|
||||||
/// <*> m <| Argument(usage: "the log to read")
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
public protocol OptionsProtocol {
|
|
||||||
associatedtype ClientError: Error
|
|
||||||
|
|
||||||
/// Evaluates this set of options in the given mode.
|
|
||||||
///
|
|
||||||
/// Returns the parsed options or a `UsageError`.
|
|
||||||
static func evaluate(_ m: CommandMode) -> Result<Self, CommandantError<ClientError>>
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An `OptionsProtocol` that has no options.
|
|
||||||
public struct NoOptions<ClientError: Error>: OptionsProtocol {
|
|
||||||
public init() {}
|
|
||||||
|
|
||||||
public static func evaluate(_ m: CommandMode) -> Result<NoOptions, CommandantError<ClientError>> {
|
|
||||||
return .success(NoOptions())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes an option that can be provided on the command line.
|
|
||||||
public struct Option<T> {
|
|
||||||
/// The key that controls this option. For example, a key of `verbose` would
|
|
||||||
/// be used for a `--verbose` option.
|
|
||||||
public let key: String
|
|
||||||
|
|
||||||
/// The default value for this option. This is the value that will be used
|
|
||||||
/// if the option is never explicitly specified on the command line.
|
|
||||||
public let defaultValue: T
|
|
||||||
|
|
||||||
/// A human-readable string describing the purpose of this option. This will
|
|
||||||
/// be shown in help messages.
|
|
||||||
///
|
|
||||||
/// For boolean operations, this should describe the effect of _not_ using
|
|
||||||
/// the default value (i.e., what will happen if you disable/enable the flag
|
|
||||||
/// differently from the default).
|
|
||||||
public let usage: String
|
|
||||||
|
|
||||||
public init(key: String, defaultValue: T, usage: String) {
|
|
||||||
self.key = key
|
|
||||||
self.defaultValue = defaultValue
|
|
||||||
self.usage = usage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Option: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
return "--\(key)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Operators
|
|
||||||
|
|
||||||
// Inspired by the Argo library:
|
|
||||||
// https://github.com/thoughtbot/Argo
|
|
||||||
/*
|
|
||||||
Copyright (c) 2014 thoughtbot, inc.
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
|
||||||
a copy of this software and associated documentation files (the
|
|
||||||
"Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish,
|
|
||||||
distribute, sublicense, and/or sell copies of the Software, and to
|
|
||||||
permit persons to whom the Software is furnished to do so, subject to
|
|
||||||
the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be
|
|
||||||
included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
||||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
||||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
||||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
||||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
||||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
||||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
infix operator <*> : LogicalDisjunctionPrecedence
|
|
||||||
|
|
||||||
infix operator <| : MultiplicationPrecedence
|
|
||||||
|
|
||||||
/// Applies `f` to the value in the given result.
|
|
||||||
///
|
|
||||||
/// In the context of command-line option parsing, this is used to chain
|
|
||||||
/// together the parsing of multiple arguments. See OptionsProtocol for an example.
|
|
||||||
public func <*> <T, U, ClientError>(f: (T) -> U, value: Result<T, CommandantError<ClientError>>) -> Result<U, CommandantError<ClientError>> {
|
|
||||||
return value.map(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Applies the function in `f` to the value in the given result.
|
|
||||||
///
|
|
||||||
/// In the context of command-line option parsing, this is used to chain
|
|
||||||
/// together the parsing of multiple arguments. See OptionsProtocol for an example.
|
|
||||||
public func <*> <T, U, ClientError>(f: Result<((T) -> U), CommandantError<ClientError>>, value: Result<T, CommandantError<ClientError>>) -> Result<U, CommandantError<ClientError>> {
|
|
||||||
switch (f, value) {
|
|
||||||
case let (.failure(left), .failure(right)):
|
|
||||||
return .failure(combineUsageErrors(left, right))
|
|
||||||
|
|
||||||
case let (.failure(left), .success):
|
|
||||||
return .failure(left)
|
|
||||||
|
|
||||||
case let (.success, .failure(right)):
|
|
||||||
return .failure(right)
|
|
||||||
|
|
||||||
case let (.success(f), .success(value)):
|
|
||||||
let newValue = f(value)
|
|
||||||
return .success(newValue)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CommandMode {
|
|
||||||
/// Evaluates the given option in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, the option's `defaultValue` is used.
|
|
||||||
public static func <| <T: ArgumentProtocol, ClientError>(mode: CommandMode, option: Option<T>) -> Result<T, CommandantError<ClientError>> {
|
|
||||||
let wrapped = Option<T?>(key: option.key, defaultValue: option.defaultValue, usage: option.usage)
|
|
||||||
// Since we are passing a non-nil default value, we can safely unwrap the
|
|
||||||
// result.
|
|
||||||
return (mode <| wrapped).map { $0! }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given option in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, `nil` is used.
|
|
||||||
public static func <| <T: ArgumentProtocol, ClientError>(mode: CommandMode, option: Option<T?>) -> Result<T?, CommandantError<ClientError>> {
|
|
||||||
let key = option.key
|
|
||||||
switch mode {
|
|
||||||
case let .arguments(arguments):
|
|
||||||
var stringValue: String?
|
|
||||||
switch arguments.consumeValue(forKey: key) {
|
|
||||||
case let .success(value):
|
|
||||||
stringValue = value
|
|
||||||
|
|
||||||
case let .failure(error):
|
|
||||||
switch error {
|
|
||||||
case let .usageError(description):
|
|
||||||
return .failure(.usageError(description: description))
|
|
||||||
|
|
||||||
case .commandError:
|
|
||||||
fatalError("CommandError should be impossible when parameterized over NoError")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let stringValue = stringValue {
|
|
||||||
if let value = T.from(string: stringValue) {
|
|
||||||
return .success(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
let description = "Invalid value for '--\(key)': \(stringValue)"
|
|
||||||
return .failure(.usageError(description: description))
|
|
||||||
} else {
|
|
||||||
return .success(option.defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
case .usage:
|
|
||||||
return .failure(informativeUsageError(option))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given option in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, the option's `defaultValue` is used.
|
|
||||||
public static func <| <T: ArgumentProtocol, ClientError>(mode: CommandMode, option: Option<[T]>) -> Result<[T], CommandantError<ClientError>> {
|
|
||||||
let wrapped = Option<[T]?>(key: option.key, defaultValue: option.defaultValue, usage: option.usage)
|
|
||||||
// Since we are passing a non-nil default value, we can safely unwrap the
|
|
||||||
// result.
|
|
||||||
return (mode <| wrapped).map { $0! }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given option in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, `nil` is used.
|
|
||||||
public static func <| <T: ArgumentProtocol, ClientError>(mode: CommandMode, option: Option<[T]?>) -> Result<[T]?, CommandantError<ClientError>> {
|
|
||||||
let key = option.key
|
|
||||||
|
|
||||||
switch mode {
|
|
||||||
case let .arguments(arguments):
|
|
||||||
let stringValue: String?
|
|
||||||
switch arguments.consumeValue(forKey: key) {
|
|
||||||
case let .success(value):
|
|
||||||
stringValue = value
|
|
||||||
|
|
||||||
case let .failure(error):
|
|
||||||
switch error {
|
|
||||||
case let .usageError(description):
|
|
||||||
return .failure(.usageError(description: description))
|
|
||||||
|
|
||||||
case .commandError:
|
|
||||||
fatalError("CommandError should be impossible when parameterized over NoError")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let unwrappedStringValue = stringValue else {
|
|
||||||
return .success(option.defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
let components = unwrappedStringValue.split(
|
|
||||||
omittingEmptySubsequences: true,
|
|
||||||
whereSeparator: [",", " "].contains
|
|
||||||
)
|
|
||||||
var resultValues: [T] = []
|
|
||||||
for component in components {
|
|
||||||
guard let value = T.from(string: String(component)) else {
|
|
||||||
let description = "Invalid value for '--\(key)': \(unwrappedStringValue)"
|
|
||||||
return .failure(.usageError(description: description))
|
|
||||||
}
|
|
||||||
resultValues.append(value)
|
|
||||||
}
|
|
||||||
return .success(resultValues)
|
|
||||||
|
|
||||||
case .usage:
|
|
||||||
return .failure(informativeUsageError(option))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluates the given boolean option in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, the option's `defaultValue` is used.
|
|
||||||
public static func <| <ClientError>(mode: CommandMode, option: Option<Bool>) -> Result<Bool, CommandantError<ClientError>> {
|
|
||||||
switch mode {
|
|
||||||
case let .arguments(arguments):
|
|
||||||
if let value = arguments.consumeBoolean(forKey: option.key) {
|
|
||||||
return .success(value)
|
|
||||||
} else {
|
|
||||||
return .success(option.defaultValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
case .usage:
|
|
||||||
return .failure(informativeUsageError(option))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,51 +0,0 @@
|
||||||
/// A poor man's ordered set.
|
|
||||||
internal struct OrderedSet<T: Hashable> {
|
|
||||||
fileprivate var values: [T] = []
|
|
||||||
|
|
||||||
init<S: Sequence>(_ sequence: S) where S.Element == T {
|
|
||||||
for e in sequence where !values.contains(e) {
|
|
||||||
values.append(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@discardableResult
|
|
||||||
mutating func remove(_ member: T) -> T? {
|
|
||||||
if let index = values.index(of: member) {
|
|
||||||
return values.remove(at: index)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension OrderedSet: Equatable {
|
|
||||||
static func == (_ lhs: OrderedSet, rhs: OrderedSet) -> Bool {
|
|
||||||
return lhs.values == rhs.values
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension OrderedSet: Collection {
|
|
||||||
subscript(position: Int) -> T {
|
|
||||||
return values[position]
|
|
||||||
}
|
|
||||||
|
|
||||||
var count: Int {
|
|
||||||
return values.count
|
|
||||||
}
|
|
||||||
|
|
||||||
var isEmpty: Bool {
|
|
||||||
return values.isEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
var startIndex: Int {
|
|
||||||
return values.startIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
var endIndex: Int {
|
|
||||||
return values.endIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
func index(after i: Int) -> Int {
|
|
||||||
return values.index(after: i)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
//
|
|
||||||
// Switch.swift
|
|
||||||
// Commandant
|
|
||||||
//
|
|
||||||
// Created by Neil Pankey on 3/31/15.
|
|
||||||
// Copyright (c) 2015 Carthage. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
/// Describes a parameterless command line flag that defaults to false and can only
|
|
||||||
/// be switched on. Canonical examples include `--force` and `--recurse`.
|
|
||||||
///
|
|
||||||
/// For a boolean toggle that can be enabled and disabled use `Option<Bool>`.
|
|
||||||
public struct Switch {
|
|
||||||
/// The key that enables this switch. For example, a key of `verbose` would be
|
|
||||||
/// used for a `--verbose` option.
|
|
||||||
public let key: String
|
|
||||||
|
|
||||||
/// Optional single letter flag that enables this switch. For example, `-v` would
|
|
||||||
/// be used as a shorthand for `--verbose`.
|
|
||||||
///
|
|
||||||
/// Multiple flags can be grouped together as a single argument and will split
|
|
||||||
/// when parsing (e.g. `rm -rf` treats 'r' and 'f' as inidividual flags).
|
|
||||||
public let flag: Character?
|
|
||||||
|
|
||||||
/// A human-readable string describing the purpose of this option. This will
|
|
||||||
/// be shown in help messages.
|
|
||||||
public let usage: String
|
|
||||||
|
|
||||||
public init(flag: Character? = nil, key: String, usage: String) {
|
|
||||||
self.flag = flag
|
|
||||||
self.key = key
|
|
||||||
self.usage = usage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Switch: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
var options = "--\(key)"
|
|
||||||
if let flag = self.flag {
|
|
||||||
options += "|-\(flag)"
|
|
||||||
}
|
|
||||||
return options
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Operators
|
|
||||||
|
|
||||||
extension CommandMode {
|
|
||||||
/// Evaluates the given boolean switch in the given mode.
|
|
||||||
///
|
|
||||||
/// If parsing command line arguments, and no value was specified on the command
|
|
||||||
/// line, the option's `defaultValue` is used.
|
|
||||||
public static func <| <ClientError> (mode: CommandMode, option: Switch) -> Result<Bool, CommandantError<ClientError>> {
|
|
||||||
switch mode {
|
|
||||||
case let .arguments(arguments):
|
|
||||||
var enabled = arguments.consume(key: option.key)
|
|
||||||
|
|
||||||
if let flag = option.flag, !enabled {
|
|
||||||
enabled = arguments.consumeBoolean(flag: flag)
|
|
||||||
}
|
|
||||||
return .success(enabled)
|
|
||||||
|
|
||||||
case .usage:
|
|
||||||
return .failure(informativeUsageError(option.description, usage: option.usage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
// Copyright (c) 2015 Rob Rix. All rights reserved.
|
|
||||||
|
|
||||||
/// Project version number for Result.
|
|
||||||
extern double ResultVersionNumber;
|
|
||||||
|
|
||||||
/// Project version string for Result.
|
|
||||||
extern const unsigned char ResultVersionString[];
|
|
||||||
|
|
|
@ -1,276 +0,0 @@
|
||||||
// Copyright (c) 2015 Rob Rix. All rights reserved.
|
|
||||||
|
|
||||||
/// An enum representing either a failure with an explanatory error, or a success with a result value.
|
|
||||||
public enum Result<T, Error: Swift.Error>: ResultProtocol, CustomStringConvertible, CustomDebugStringConvertible {
|
|
||||||
case success(T)
|
|
||||||
case failure(Error)
|
|
||||||
|
|
||||||
// MARK: Constructors
|
|
||||||
|
|
||||||
/// Constructs a success wrapping a `value`.
|
|
||||||
public init(value: T) {
|
|
||||||
self = .success(value)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a failure wrapping an `error`.
|
|
||||||
public init(error: Error) {
|
|
||||||
self = .failure(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a result from an `Optional`, failing with `Error` if `nil`.
|
|
||||||
public init(_ value: T?, failWith: @autoclosure () -> Error) {
|
|
||||||
self = value.map(Result.success) ?? .failure(failWith())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a result from a function that uses `throw`, failing with `Error` if throws.
|
|
||||||
public init(_ f: @autoclosure () throws -> T) {
|
|
||||||
self.init(attempt: f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a result from a function that uses `throw`, failing with `Error` if throws.
|
|
||||||
public init(attempt f: () throws -> T) {
|
|
||||||
do {
|
|
||||||
self = .success(try f())
|
|
||||||
} catch var error {
|
|
||||||
if Error.self == AnyError.self {
|
|
||||||
error = AnyError(error)
|
|
||||||
}
|
|
||||||
self = .failure(error as! Error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Deconstruction
|
|
||||||
|
|
||||||
/// Returns the value from `success` Results or `throw`s the error.
|
|
||||||
public func dematerialize() throws -> T {
|
|
||||||
switch self {
|
|
||||||
case let .success(value):
|
|
||||||
return value
|
|
||||||
case let .failure(error):
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Case analysis for Result.
|
|
||||||
///
|
|
||||||
/// Returns the value produced by applying `ifFailure` to `failure` Results, or `ifSuccess` to `success` Results.
|
|
||||||
public func analysis<Result>(ifSuccess: (T) -> Result, ifFailure: (Error) -> Result) -> Result {
|
|
||||||
switch self {
|
|
||||||
case let .success(value):
|
|
||||||
return ifSuccess(value)
|
|
||||||
case let .failure(value):
|
|
||||||
return ifFailure(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Errors
|
|
||||||
|
|
||||||
/// The domain for errors constructed by Result.
|
|
||||||
public static var errorDomain: String { return "com.antitypical.Result" }
|
|
||||||
|
|
||||||
/// The userInfo key for source functions in errors constructed by Result.
|
|
||||||
public static var functionKey: String { return "\(errorDomain).function" }
|
|
||||||
|
|
||||||
/// The userInfo key for source file paths in errors constructed by Result.
|
|
||||||
public static var fileKey: String { return "\(errorDomain).file" }
|
|
||||||
|
|
||||||
/// The userInfo key for source file line numbers in errors constructed by Result.
|
|
||||||
public static var lineKey: String { return "\(errorDomain).line" }
|
|
||||||
|
|
||||||
/// Constructs an error.
|
|
||||||
public static func error(_ message: String? = nil, function: String = #function, file: String = #file, line: Int = #line) -> NSError {
|
|
||||||
var userInfo: [String: Any] = [
|
|
||||||
functionKey: function,
|
|
||||||
fileKey: file,
|
|
||||||
lineKey: line,
|
|
||||||
]
|
|
||||||
|
|
||||||
if let message = message {
|
|
||||||
userInfo[NSLocalizedDescriptionKey] = message
|
|
||||||
}
|
|
||||||
|
|
||||||
return NSError(domain: errorDomain, code: 0, userInfo: userInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: CustomStringConvertible
|
|
||||||
|
|
||||||
public var description: String {
|
|
||||||
return analysis(
|
|
||||||
ifSuccess: { ".success(\($0))" },
|
|
||||||
ifFailure: { ".failure(\($0))" })
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// MARK: CustomDebugStringConvertible
|
|
||||||
|
|
||||||
public var debugDescription: String {
|
|
||||||
return description
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Derive result from failable closure
|
|
||||||
|
|
||||||
public func materialize<T>(_ f: () throws -> T) -> Result<T, AnyError> {
|
|
||||||
return materialize(try f())
|
|
||||||
}
|
|
||||||
|
|
||||||
public func materialize<T>(_ f: @autoclosure () throws -> T) -> Result<T, AnyError> {
|
|
||||||
do {
|
|
||||||
return .success(try f())
|
|
||||||
} catch {
|
|
||||||
return .failure(AnyError(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use the overload which returns `Result<T, AnyError>` instead")
|
|
||||||
public func materialize<T>(_ f: () throws -> T) -> Result<T, NSError> {
|
|
||||||
return materialize(try f())
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(*, deprecated, message: "Use the overload which returns `Result<T, AnyError>` instead")
|
|
||||||
public func materialize<T>(_ f: @autoclosure () throws -> T) -> Result<T, NSError> {
|
|
||||||
do {
|
|
||||||
return .success(try f())
|
|
||||||
} catch {
|
|
||||||
// This isn't great, but it lets us maintain compatibility until this deprecated
|
|
||||||
// method can be removed.
|
|
||||||
#if _runtime(_ObjC)
|
|
||||||
return .failure(error as NSError)
|
|
||||||
#else
|
|
||||||
// https://github.com/apple/swift-corelibs-foundation/blob/swift-3.0.2-RELEASE/Foundation/NSError.swift#L314
|
|
||||||
let userInfo = _swift_Foundation_getErrorDefaultUserInfo(error) as? [String: Any]
|
|
||||||
let nsError = NSError(domain: error._domain, code: error._code, userInfo: userInfo)
|
|
||||||
return .failure(nsError)
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Cocoa API conveniences
|
|
||||||
|
|
||||||
#if !os(Linux)
|
|
||||||
|
|
||||||
/// Constructs a `Result` with the result of calling `try` with an error pointer.
|
|
||||||
///
|
|
||||||
/// This is convenient for wrapping Cocoa API which returns an object or `nil` + an error, by reference. e.g.:
|
|
||||||
///
|
|
||||||
/// Result.try { NSData(contentsOfURL: URL, options: .dataReadingMapped, error: $0) }
|
|
||||||
@available(*, deprecated, message: "This will be removed in Result 4.0. Use `Result.init(attempt:)` instead. See https://github.com/antitypical/Result/issues/85 for the details.")
|
|
||||||
public func `try`<T>(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> T?) -> Result<T, NSError> {
|
|
||||||
var error: NSError?
|
|
||||||
return `try`(&error).map(Result.success) ?? .failure(error ?? Result<T, NSError>.error(function: function, file: file, line: line))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a `Result` with the result of calling `try` with an error pointer.
|
|
||||||
///
|
|
||||||
/// This is convenient for wrapping Cocoa API which returns a `Bool` + an error, by reference. e.g.:
|
|
||||||
///
|
|
||||||
/// Result.try { NSFileManager.defaultManager().removeItemAtURL(URL, error: $0) }
|
|
||||||
@available(*, deprecated, message: "This will be removed in Result 4.0. Use `Result.init(attempt:)` instead. See https://github.com/antitypical/Result/issues/85 for the details.")
|
|
||||||
public func `try`(_ function: String = #function, file: String = #file, line: Int = #line, `try`: (NSErrorPointer) -> Bool) -> Result<(), NSError> {
|
|
||||||
var error: NSError?
|
|
||||||
return `try`(&error) ?
|
|
||||||
.success(())
|
|
||||||
: .failure(error ?? Result<(), NSError>.error(function: function, file: file, line: line))
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - ErrorConvertible conformance
|
|
||||||
|
|
||||||
extension NSError: ErrorConvertible {
|
|
||||||
public static func error(from error: Swift.Error) -> Self {
|
|
||||||
func cast<T: NSError>(_ error: Swift.Error) -> T {
|
|
||||||
return error as! T
|
|
||||||
}
|
|
||||||
|
|
||||||
return cast(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Errors
|
|
||||||
|
|
||||||
/// An “error” that is impossible to construct.
|
|
||||||
///
|
|
||||||
/// This can be used to describe `Result`s where failures will never
|
|
||||||
/// be generated. For example, `Result<Int, NoError>` describes a result that
|
|
||||||
/// contains an `Int`eger and is guaranteed never to be a `failure`.
|
|
||||||
public enum NoError: Swift.Error, Equatable {
|
|
||||||
public static func ==(lhs: NoError, rhs: NoError) -> Bool {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A type-erased error which wraps an arbitrary error instance. This should be
|
|
||||||
/// useful for generic contexts.
|
|
||||||
public struct AnyError: Swift.Error {
|
|
||||||
/// The underlying error.
|
|
||||||
public let error: Swift.Error
|
|
||||||
|
|
||||||
public init(_ error: Swift.Error) {
|
|
||||||
if let anyError = error as? AnyError {
|
|
||||||
self = anyError
|
|
||||||
} else {
|
|
||||||
self.error = error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AnyError: ErrorConvertible {
|
|
||||||
public static func error(from error: Error) -> AnyError {
|
|
||||||
return AnyError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension AnyError: CustomStringConvertible {
|
|
||||||
public var description: String {
|
|
||||||
return String(describing: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// There appears to be a bug in Foundation on Linux which prevents this from working:
|
|
||||||
// https://bugs.swift.org/browse/SR-3565
|
|
||||||
// Don't forget to comment the tests back in when removing this check when it's fixed!
|
|
||||||
#if !os(Linux)
|
|
||||||
|
|
||||||
extension AnyError: LocalizedError {
|
|
||||||
public var errorDescription: String? {
|
|
||||||
return error.localizedDescription
|
|
||||||
}
|
|
||||||
|
|
||||||
public var failureReason: String? {
|
|
||||||
return (error as? LocalizedError)?.failureReason
|
|
||||||
}
|
|
||||||
|
|
||||||
public var helpAnchor: String? {
|
|
||||||
return (error as? LocalizedError)?.helpAnchor
|
|
||||||
}
|
|
||||||
|
|
||||||
public var recoverySuggestion: String? {
|
|
||||||
return (error as? LocalizedError)?.recoverySuggestion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// MARK: - migration support
|
|
||||||
extension Result {
|
|
||||||
@available(*, unavailable, renamed: "success")
|
|
||||||
public static func Success(_: T) -> Result<T, Error> {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(*, unavailable, renamed: "failure")
|
|
||||||
public static func Failure(_: Error) -> Result<T, Error> {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension NSError {
|
|
||||||
@available(*, unavailable, renamed: "error(from:)")
|
|
||||||
public static func errorFromErrorType(_ error: Swift.Error) -> Self {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
import Foundation
|
|
|
@ -1,203 +0,0 @@
|
||||||
// Copyright (c) 2015 Rob Rix. All rights reserved.
|
|
||||||
|
|
||||||
/// A type that can represent either failure with an error or success with a result value.
|
|
||||||
public protocol ResultProtocol {
|
|
||||||
associatedtype Value
|
|
||||||
associatedtype Error: Swift.Error
|
|
||||||
|
|
||||||
/// Constructs a successful result wrapping a `value`.
|
|
||||||
init(value: Value)
|
|
||||||
|
|
||||||
/// Constructs a failed result wrapping an `error`.
|
|
||||||
init(error: Error)
|
|
||||||
|
|
||||||
/// Case analysis for ResultProtocol.
|
|
||||||
///
|
|
||||||
/// Returns the value produced by appliying `ifFailure` to the error if self represents a failure, or `ifSuccess` to the result value if self represents a success.
|
|
||||||
func analysis<U>(ifSuccess: (Value) -> U, ifFailure: (Error) -> U) -> U
|
|
||||||
|
|
||||||
/// Returns the value if self represents a success, `nil` otherwise.
|
|
||||||
///
|
|
||||||
/// A default implementation is provided by a protocol extension. Conforming types may specialize it.
|
|
||||||
var value: Value? { get }
|
|
||||||
|
|
||||||
/// Returns the error if self represents a failure, `nil` otherwise.
|
|
||||||
///
|
|
||||||
/// A default implementation is provided by a protocol extension. Conforming types may specialize it.
|
|
||||||
var error: Error? { get }
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension ResultProtocol {
|
|
||||||
|
|
||||||
/// Returns the value if self represents a success, `nil` otherwise.
|
|
||||||
public var value: Value? {
|
|
||||||
return analysis(ifSuccess: { $0 }, ifFailure: { _ in nil })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the error if self represents a failure, `nil` otherwise.
|
|
||||||
public var error: Error? {
|
|
||||||
return analysis(ifSuccess: { _ in nil }, ifFailure: { $0 })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new Result by mapping `Success`es’ values using `transform`, or re-wrapping `Failure`s’ errors.
|
|
||||||
public func map<U>(_ transform: (Value) -> U) -> Result<U, Error> {
|
|
||||||
return flatMap { .success(transform($0)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors.
|
|
||||||
public func flatMap<U>(_ transform: (Value) -> Result<U, Error>) -> Result<U, Error> {
|
|
||||||
return analysis(
|
|
||||||
ifSuccess: transform,
|
|
||||||
ifFailure: Result<U, Error>.failure)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a Result with a tuple of the receiver and `other` values if both
|
|
||||||
/// are `Success`es, or re-wrapping the error of the earlier `Failure`.
|
|
||||||
public func fanout<R: ResultProtocol>(_ other: @autoclosure () -> R) -> Result<(Value, R.Value), Error>
|
|
||||||
where Error == R.Error
|
|
||||||
{
|
|
||||||
return self.flatMap { left in other().map { right in (left, right) } }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new Result by mapping `Failure`'s values using `transform`, or re-wrapping `Success`es’ values.
|
|
||||||
public func mapError<Error2>(_ transform: (Error) -> Error2) -> Result<Value, Error2> {
|
|
||||||
return flatMapError { .failure(transform($0)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the result of applying `transform` to `Failure`’s errors, or re-wrapping `Success`es’ values.
|
|
||||||
public func flatMapError<Error2>(_ transform: (Error) -> Result<Value, Error2>) -> Result<Value, Error2> {
|
|
||||||
return analysis(
|
|
||||||
ifSuccess: Result<Value, Error2>.success,
|
|
||||||
ifFailure: transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new Result by mapping `Success`es’ values using `success`, and by mapping `Failure`'s values using `failure`.
|
|
||||||
public func bimap<U, Error2>(success: (Value) -> U, failure: (Error) -> Error2) -> Result<U, Error2> {
|
|
||||||
return analysis(
|
|
||||||
ifSuccess: { .success(success($0)) },
|
|
||||||
ifFailure: { .failure(failure($0)) }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension ResultProtocol {
|
|
||||||
|
|
||||||
// MARK: Higher-order functions
|
|
||||||
|
|
||||||
/// Returns `self.value` if this result is a .Success, or the given value otherwise. Equivalent with `??`
|
|
||||||
public func recover(_ value: @autoclosure () -> Value) -> Value {
|
|
||||||
return self.value ?? value()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns this result if it is a .Success, or the given result otherwise. Equivalent with `??`
|
|
||||||
public func recover(with result: @autoclosure () -> Self) -> Self {
|
|
||||||
return analysis(
|
|
||||||
ifSuccess: { _ in self },
|
|
||||||
ifFailure: { _ in result() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Protocol used to constrain `tryMap` to `Result`s with compatible `Error`s.
|
|
||||||
public protocol ErrorConvertible: Swift.Error {
|
|
||||||
static func error(from error: Swift.Error) -> Self
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension ResultProtocol where Error: ErrorConvertible {
|
|
||||||
|
|
||||||
/// Returns the result of applying `transform` to `Success`es’ values, or wrapping thrown errors.
|
|
||||||
public func tryMap<U>(_ transform: (Value) throws -> U) -> Result<U, Error> {
|
|
||||||
return flatMap { value in
|
|
||||||
do {
|
|
||||||
return .success(try transform(value))
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
let convertedError = Error.error(from: error)
|
|
||||||
// Revisit this in a future version of Swift. https://twitter.com/jckarter/status/672931114944696321
|
|
||||||
return .failure(convertedError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Operators
|
|
||||||
|
|
||||||
infix operator &&& : LogicalConjunctionPrecedence
|
|
||||||
|
|
||||||
/// Returns a Result with a tuple of `left` and `right` values if both are `Success`es, or re-wrapping the error of the earlier `Failure`.
|
|
||||||
@available(*, deprecated, renamed: "ResultProtocol.fanout(self:_:)")
|
|
||||||
public func &&& <L: ResultProtocol, R: ResultProtocol> (left: L, right: @autoclosure () -> R) -> Result<(L.Value, R.Value), L.Error>
|
|
||||||
where L.Error == R.Error
|
|
||||||
{
|
|
||||||
return left.fanout(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
precedencegroup ChainingPrecedence {
|
|
||||||
associativity: left
|
|
||||||
higherThan: TernaryPrecedence
|
|
||||||
}
|
|
||||||
|
|
||||||
infix operator >>- : ChainingPrecedence
|
|
||||||
|
|
||||||
/// Returns the result of applying `transform` to `Success`es’ values, or re-wrapping `Failure`’s errors.
|
|
||||||
///
|
|
||||||
/// This is a synonym for `flatMap`.
|
|
||||||
@available(*, deprecated, renamed: "ResultProtocol.flatMap(self:_:)")
|
|
||||||
public func >>- <T: ResultProtocol, U> (result: T, transform: (T.Value) -> Result<U, T.Error>) -> Result<U, T.Error> {
|
|
||||||
return result.flatMap(transform)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if `left` and `right` are both `Success`es and their values are equal, or if `left` and `right` are both `Failure`s and their errors are equal.
|
|
||||||
public func == <T: ResultProtocol> (left: T, right: T) -> Bool
|
|
||||||
where T.Value: Equatable, T.Error: Equatable
|
|
||||||
{
|
|
||||||
if let left = left.value, let right = right.value {
|
|
||||||
return left == right
|
|
||||||
} else if let left = left.error, let right = right.error {
|
|
||||||
return left == right
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if `left` and `right` represent different cases, or if they represent the same case but different values.
|
|
||||||
public func != <T: ResultProtocol> (left: T, right: T) -> Bool
|
|
||||||
where T.Value: Equatable, T.Error: Equatable
|
|
||||||
{
|
|
||||||
return !(left == right)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the value of `left` if it is a `Success`, or `right` otherwise. Short-circuits.
|
|
||||||
public func ?? <T: ResultProtocol> (left: T, right: @autoclosure () -> T.Value) -> T.Value {
|
|
||||||
return left.recover(right())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `left` if it is a `Success`es, or `right` otherwise. Short-circuits.
|
|
||||||
public func ?? <T: ResultProtocol> (left: T, right: @autoclosure () -> T) -> T {
|
|
||||||
return left.recover(with: right())
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - migration support
|
|
||||||
@available(*, unavailable, renamed: "ResultProtocol")
|
|
||||||
public typealias ResultType = ResultProtocol
|
|
||||||
|
|
||||||
@available(*, unavailable, renamed: "Error")
|
|
||||||
public typealias ResultErrorType = Swift.Error
|
|
||||||
|
|
||||||
@available(*, unavailable, renamed: "ErrorConvertible")
|
|
||||||
public typealias ErrorTypeConvertible = ErrorConvertible
|
|
||||||
|
|
||||||
@available(*, deprecated, renamed: "ErrorConvertible")
|
|
||||||
public protocol ErrorProtocolConvertible: ErrorConvertible {}
|
|
||||||
|
|
||||||
extension ResultProtocol {
|
|
||||||
@available(*, unavailable, renamed: "recover(with:)")
|
|
||||||
public func recoverWith(_ result: @autoclosure () -> Self) -> Self {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ErrorConvertible {
|
|
||||||
@available(*, unavailable, renamed: "error(from:)")
|
|
||||||
public static func errorFromErrorType(_ error: Swift.Error) -> Self {
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
---
|
|
||||||
SEEDS:
|
|
||||||
- Commandant (0.13.0)
|
|
||||||
- Result (3.2.4)
|
|
||||||
- Nimble (v7.0.3)
|
|
||||||
- Quick (v1.2.0)
|
|
|
@ -7,30 +7,11 @@
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
073998C12AE3BEEC41CE0DAD /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9871C2273F4D762A1F19B07 /* OrderedSet.swift */; };
|
|
||||||
09756A23E9102359296C821B /* ResultProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5150F7FB7CF2A77F675D8E92 /* ResultProtocol.swift */; };
|
|
||||||
09A04CB22DC02B86AE4ACC8A /* ArgumentParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9D96DDBBCCCC5944160ABE /* ArgumentParser.swift */; };
|
|
||||||
0C47E694564FCB59996690DD /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E4D331CCD66ADFE19CE39 /* Command.swift */; };
|
|
||||||
0EBF5CDD379D7462C3389536 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9257C5FABA335E5F060CB7F7 /* Result.swift */; };
|
|
||||||
15E27926A580EABEB1B218EF /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1B6BEDF32AF3F8A575FB1F /* Switch.swift */; };
|
|
||||||
27340A5BB9F2A5B166E3A72A /* ArgumentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4237E5AA1A289D03D2A2FB8 /* ArgumentProtocol.swift */; };
|
|
||||||
3053D11E74A22A4C5A6BE833 /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F547B3DC473CFB1BE0AEB70A /* Errors.swift */; };
|
|
||||||
30EA893640B02CCF679F9C57 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD7FE171F643805F7BC38A7 /* Option.swift */; };
|
|
||||||
3F177C62A7053BA3ED415E5E /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36A4ABD8025E13060312925 /* Argument.swift */; };
|
|
||||||
4913269B1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913269A1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift */; };
|
4913269B1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913269A1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift */; };
|
||||||
49C2F3FDD805256BE934A70E /* Switch.swift in Sources */ = {isa = PBXBuildFile; fileRef = AF1B6BEDF32AF3F8A575FB1F /* Switch.swift */; };
|
|
||||||
4C8321353B9AE40539A1AC8A /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = F547B3DC473CFB1BE0AEB70A /* Errors.swift */; };
|
|
||||||
693A98991CBFFA760004D3B4 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A98981CBFFA760004D3B4 /* Search.swift */; };
|
693A98991CBFFA760004D3B4 /* Search.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A98981CBFFA760004D3B4 /* Search.swift */; };
|
||||||
693A989B1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A989A1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift */; };
|
693A989B1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693A989A1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift */; };
|
||||||
8078FAA81EC4F2FB004B5B3F /* Lucky.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8078FAA71EC4F2FB004B5B3F /* Lucky.swift */; };
|
8078FAA81EC4F2FB004B5B3F /* Lucky.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8078FAA71EC4F2FB004B5B3F /* Lucky.swift */; };
|
||||||
900A1E811DBAC8CB0069B1A8 /* Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = 900A1E801DBAC8CB0069B1A8 /* Info.swift */; };
|
900A1E811DBAC8CB0069B1A8 /* Info.swift in Sources */ = {isa = PBXBuildFile; fileRef = 900A1E801DBAC8CB0069B1A8 /* Info.swift */; };
|
||||||
92AE0FD7BE06D64692E6C1E6 /* OrderedSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9871C2273F4D762A1F19B07 /* OrderedSet.swift */; };
|
|
||||||
AD0785BC0EC6BBF4ED560DCC /* ArgumentParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9D96DDBBCCCC5944160ABE /* ArgumentParser.swift */; };
|
|
||||||
ADE553C828AF4EAFF39ED3E1 /* ArgumentProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4237E5AA1A289D03D2A2FB8 /* ArgumentProtocol.swift */; };
|
|
||||||
C50DD25454FC5CAA1F37763F /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9257C5FABA335E5F060CB7F7 /* Result.swift */; };
|
|
||||||
DE6E193A6671F3D6807F746D /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326E4D331CCD66ADFE19CE39 /* Command.swift */; };
|
|
||||||
EBD6B44FDF65E0253153629F /* HelpCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FDC2B8063EC231E28353D23 /* HelpCommand.swift */; };
|
|
||||||
EC113CE6C98E8D8358228D33 /* ResultProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5150F7FB7CF2A77F675D8E92 /* ResultProtocol.swift */; };
|
|
||||||
ED031A7C1B5127C00097692E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED031A7B1B5127C00097692E /* main.swift */; };
|
ED031A7C1B5127C00097692E /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED031A7B1B5127C00097692E /* main.swift */; };
|
||||||
ED0F237F1B87522400AE40CD /* Install.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F237E1B87522400AE40CD /* Install.swift */; };
|
ED0F237F1B87522400AE40CD /* Install.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F237E1B87522400AE40CD /* Install.swift */; };
|
||||||
ED0F23831B87533A00AE40CD /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23821B87533A00AE40CD /* List.swift */; };
|
ED0F23831B87533A00AE40CD /* List.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F23821B87533A00AE40CD /* List.swift */; };
|
||||||
|
@ -49,9 +30,6 @@
|
||||||
EDE296531C700F4300554778 /* SignOut.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE296521C700F4300554778 /* SignOut.swift */; };
|
EDE296531C700F4300554778 /* SignOut.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDE296521C700F4300554778 /* SignOut.swift */; };
|
||||||
EDEAA0C01B51CE6200F2FC3F /* StoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA0BF1B51CE6200F2FC3F /* StoreFoundation.framework */; };
|
EDEAA0C01B51CE6200F2FC3F /* StoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA0BF1B51CE6200F2FC3F /* StoreFoundation.framework */; };
|
||||||
EDEAA17D1B5C579100F2FC3F /* CommerceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA17C1B5C579100F2FC3F /* CommerceKit.framework */; };
|
EDEAA17D1B5C579100F2FC3F /* CommerceKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDEAA17C1B5C579100F2FC3F /* CommerceKit.framework */; };
|
||||||
F184B6B7CD9C013CACDED0FB /* Argument.swift in Sources */ = {isa = PBXBuildFile; fileRef = F36A4ABD8025E13060312925 /* Argument.swift */; };
|
|
||||||
F48562FA81B0C0258AC063B4 /* HelpCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FDC2B8063EC231E28353D23 /* HelpCommand.swift */; };
|
|
||||||
F6D2058A70757D3477185A50 /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AD7FE171F643805F7BC38A7 /* Option.swift */; };
|
|
||||||
F865880B2030F6DE0093DE57 /* MASErrorTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F865880A2030F6DE0093DE57 /* MASErrorTestCase.swift */; };
|
F865880B2030F6DE0093DE57 /* MASErrorTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = F865880A2030F6DE0093DE57 /* MASErrorTestCase.swift */; };
|
||||||
F86588272030FAE70093DE57 /* MASError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238C1B8756E600AE40CD /* MASError.swift */; };
|
F86588272030FAE70093DE57 /* MASError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED0F238C1B8756E600AE40CD /* MASError.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
@ -79,20 +57,11 @@
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
2AD7FE171F643805F7BC38A7 /* Option.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Option.swift; path = Seeds/Commandant/Sources/Commandant/Option.swift; sourceTree = "<group>"; };
|
|
||||||
326E4D331CCD66ADFE19CE39 /* Command.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Command.swift; path = Seeds/Commandant/Sources/Commandant/Command.swift; sourceTree = "<group>"; };
|
|
||||||
4913269A1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKSoftwareMap+AppLookup.swift"; sourceTree = "<group>"; };
|
4913269A1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CKSoftwareMap+AppLookup.swift"; sourceTree = "<group>"; };
|
||||||
5150F7FB7CF2A77F675D8E92 /* ResultProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ResultProtocol.swift; path = Seeds/Result/Result/ResultProtocol.swift; sourceTree = "<group>"; };
|
|
||||||
693A98981CBFFA760004D3B4 /* Search.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Search.swift; sourceTree = "<group>"; };
|
693A98981CBFFA760004D3B4 /* Search.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Search.swift; sourceTree = "<group>"; };
|
||||||
693A989A1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSession+Synchronous.swift"; sourceTree = "<group>"; };
|
693A989A1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSURLSession+Synchronous.swift"; sourceTree = "<group>"; };
|
||||||
8078FAA71EC4F2FB004B5B3F /* Lucky.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lucky.swift; sourceTree = "<group>"; };
|
8078FAA71EC4F2FB004B5B3F /* Lucky.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lucky.swift; sourceTree = "<group>"; };
|
||||||
8FDC2B8063EC231E28353D23 /* HelpCommand.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = HelpCommand.swift; path = Seeds/Commandant/Sources/Commandant/HelpCommand.swift; sourceTree = "<group>"; };
|
|
||||||
900A1E801DBAC8CB0069B1A8 /* Info.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Info.swift; sourceTree = "<group>"; };
|
900A1E801DBAC8CB0069B1A8 /* Info.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Info.swift; sourceTree = "<group>"; };
|
||||||
9257C5FABA335E5F060CB7F7 /* Result.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Result.swift; path = Seeds/Result/Result/Result.swift; sourceTree = "<group>"; };
|
|
||||||
AF1B6BEDF32AF3F8A575FB1F /* Switch.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Switch.swift; path = Seeds/Commandant/Sources/Commandant/Switch.swift; sourceTree = "<group>"; };
|
|
||||||
B4237E5AA1A289D03D2A2FB8 /* ArgumentProtocol.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ArgumentProtocol.swift; path = Seeds/Commandant/Sources/Commandant/ArgumentProtocol.swift; sourceTree = "<group>"; };
|
|
||||||
D9871C2273F4D762A1F19B07 /* OrderedSet.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OrderedSet.swift; path = Seeds/Commandant/Sources/Commandant/OrderedSet.swift; sourceTree = "<group>"; };
|
|
||||||
EA9D96DDBBCCCC5944160ABE /* ArgumentParser.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ArgumentParser.swift; path = Seeds/Commandant/Sources/Commandant/ArgumentParser.swift; sourceTree = "<group>"; };
|
|
||||||
ED031A781B5127C00097692E /* mas */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mas; sourceTree = BUILT_PRODUCTS_DIR; };
|
ED031A781B5127C00097692E /* mas */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mas; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
ED031A7B1B5127C00097692E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
ED031A7B1B5127C00097692E /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
|
||||||
ED0F237E1B87522400AE40CD /* Install.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Install.swift; sourceTree = "<group>"; };
|
ED0F237E1B87522400AE40CD /* Install.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Install.swift; sourceTree = "<group>"; };
|
||||||
|
@ -137,8 +106,6 @@
|
||||||
EDEAA1551B5C576D00F2FC3F /* CKUpdateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKUpdateController.h; sourceTree = "<group>"; };
|
EDEAA1551B5C576D00F2FC3F /* CKUpdateController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CKUpdateController.h; sourceTree = "<group>"; };
|
||||||
EDEAA1661B5C576D00F2FC3F /* ISStoreURLOperationDelegate-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ISStoreURLOperationDelegate-Protocol.h"; sourceTree = "<group>"; };
|
EDEAA1661B5C576D00F2FC3F /* ISStoreURLOperationDelegate-Protocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ISStoreURLOperationDelegate-Protocol.h"; sourceTree = "<group>"; };
|
||||||
EDEAA17C1B5C579100F2FC3F /* CommerceKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CommerceKit.framework; path = /System/Library/PrivateFrameworks/CommerceKit.framework; sourceTree = "<absolute>"; };
|
EDEAA17C1B5C579100F2FC3F /* CommerceKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CommerceKit.framework; path = /System/Library/PrivateFrameworks/CommerceKit.framework; sourceTree = "<absolute>"; };
|
||||||
F36A4ABD8025E13060312925 /* Argument.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Argument.swift; path = Seeds/Commandant/Sources/Commandant/Argument.swift; sourceTree = "<group>"; };
|
|
||||||
F547B3DC473CFB1BE0AEB70A /* Errors.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Errors.swift; path = Seeds/Commandant/Sources/Commandant/Errors.swift; sourceTree = "<group>"; };
|
|
||||||
F8233C87201EBDF000268278 /* mas-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "mas-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
F8233C87201EBDF000268278 /* mas-tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "mas-tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
F8233C8B201EBDF100268278 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
F8233C8B201EBDF100268278 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
F865880A2030F6DE0093DE57 /* MASErrorTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MASErrorTestCase.swift; sourceTree = "<group>"; };
|
F865880A2030F6DE0093DE57 /* MASErrorTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MASErrorTestCase.swift; sourceTree = "<group>"; };
|
||||||
|
@ -164,56 +131,6 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
0739FA024AB80925B32D64D1 /* Seeds */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
A49C8EB621015CD588C621FD /* Commandant */,
|
|
||||||
A96B40B5852A4F029AFDA669 /* Nimble */,
|
|
||||||
6EB4B681BB79E32622860164 /* Quick */,
|
|
||||||
6569B225928F21A4A639BB22 /* Result */,
|
|
||||||
);
|
|
||||||
name = Seeds;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
6569B225928F21A4A639BB22 /* Result */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
9257C5FABA335E5F060CB7F7 /* Result.swift */,
|
|
||||||
5150F7FB7CF2A77F675D8E92 /* ResultProtocol.swift */,
|
|
||||||
);
|
|
||||||
name = Result;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
6EB4B681BB79E32622860164 /* Quick */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
name = Quick;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A49C8EB621015CD588C621FD /* Commandant */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
F36A4ABD8025E13060312925 /* Argument.swift */,
|
|
||||||
EA9D96DDBBCCCC5944160ABE /* ArgumentParser.swift */,
|
|
||||||
B4237E5AA1A289D03D2A2FB8 /* ArgumentProtocol.swift */,
|
|
||||||
326E4D331CCD66ADFE19CE39 /* Command.swift */,
|
|
||||||
F547B3DC473CFB1BE0AEB70A /* Errors.swift */,
|
|
||||||
8FDC2B8063EC231E28353D23 /* HelpCommand.swift */,
|
|
||||||
2AD7FE171F643805F7BC38A7 /* Option.swift */,
|
|
||||||
D9871C2273F4D762A1F19B07 /* OrderedSet.swift */,
|
|
||||||
AF1B6BEDF32AF3F8A575FB1F /* Switch.swift */,
|
|
||||||
);
|
|
||||||
name = Commandant;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
A96B40B5852A4F029AFDA669 /* Nimble */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
);
|
|
||||||
name = Nimble;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
ED031A6F1B5127C00097692E = {
|
ED031A6F1B5127C00097692E = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -222,7 +139,6 @@
|
||||||
EDFC76381B642A2E00D0DBD7 /* Frameworks */,
|
EDFC76381B642A2E00D0DBD7 /* Frameworks */,
|
||||||
EDEAA0C11B51CEBD00F2FC3F /* PrivateHeaders */,
|
EDEAA0C11B51CEBD00F2FC3F /* PrivateHeaders */,
|
||||||
ED031A791B5127C00097692E /* Products */,
|
ED031A791B5127C00097692E /* Products */,
|
||||||
0739FA024AB80925B32D64D1 /* Seeds */,
|
|
||||||
);
|
);
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
@ -448,14 +364,8 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
ED0F23871B87537200AE40CD /* Account.swift in Sources */,
|
ED0F23871B87537200AE40CD /* Account.swift in Sources */,
|
||||||
F184B6B7CD9C013CACDED0FB /* Argument.swift in Sources */,
|
|
||||||
AD0785BC0EC6BBF4ED560DCC /* ArgumentParser.swift in Sources */,
|
|
||||||
ADE553C828AF4EAFF39ED3E1 /* ArgumentProtocol.swift in Sources */,
|
|
||||||
4913269B1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift in Sources */,
|
4913269B1F48921D0010EB86 /* CKSoftwareMap+AppLookup.swift in Sources */,
|
||||||
0C47E694564FCB59996690DD /* Command.swift in Sources */,
|
|
||||||
ED0F238B1B87569C00AE40CD /* Downloader.swift in Sources */,
|
ED0F238B1B87569C00AE40CD /* Downloader.swift in Sources */,
|
||||||
3053D11E74A22A4C5A6BE833 /* Errors.swift in Sources */,
|
|
||||||
EBD6B44FDF65E0253153629F /* HelpCommand.swift in Sources */,
|
|
||||||
900A1E811DBAC8CB0069B1A8 /* Info.swift in Sources */,
|
900A1E811DBAC8CB0069B1A8 /* Info.swift in Sources */,
|
||||||
ED0F237F1B87522400AE40CD /* Install.swift in Sources */,
|
ED0F237F1B87522400AE40CD /* Install.swift in Sources */,
|
||||||
ED0F23901B87A56F00AE40CD /* ISStoreAccount.swift in Sources */,
|
ED0F23901B87A56F00AE40CD /* ISStoreAccount.swift in Sources */,
|
||||||
|
@ -464,18 +374,13 @@
|
||||||
ED031A7C1B5127C00097692E /* main.swift in Sources */,
|
ED031A7C1B5127C00097692E /* main.swift in Sources */,
|
||||||
ED0F238D1B8756E600AE40CD /* MASError.swift in Sources */,
|
ED0F238D1B8756E600AE40CD /* MASError.swift in Sources */,
|
||||||
693A989B1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift in Sources */,
|
693A989B1CBFFAAA0004D3B4 /* NSURLSession+Synchronous.swift in Sources */,
|
||||||
30EA893640B02CCF679F9C57 /* Option.swift in Sources */,
|
|
||||||
92AE0FD7BE06D64692E6C1E6 /* OrderedSet.swift in Sources */,
|
|
||||||
ED0F23851B87536A00AE40CD /* Outdated.swift in Sources */,
|
ED0F23851B87536A00AE40CD /* Outdated.swift in Sources */,
|
||||||
ED0F23891B87543D00AE40CD /* PurchaseDownloadObserver.swift in Sources */,
|
ED0F23891B87543D00AE40CD /* PurchaseDownloadObserver.swift in Sources */,
|
||||||
EDCBF9531D89AC6F000039C6 /* Reset.swift in Sources */,
|
EDCBF9531D89AC6F000039C6 /* Reset.swift in Sources */,
|
||||||
0EBF5CDD379D7462C3389536 /* Result.swift in Sources */,
|
|
||||||
09756A23E9102359296C821B /* ResultProtocol.swift in Sources */,
|
|
||||||
693A98991CBFFA760004D3B4 /* Search.swift in Sources */,
|
693A98991CBFFA760004D3B4 /* Search.swift in Sources */,
|
||||||
EDC90B651C70045E0019E396 /* SignIn.swift in Sources */,
|
EDC90B651C70045E0019E396 /* SignIn.swift in Sources */,
|
||||||
EDE296531C700F4300554778 /* SignOut.swift in Sources */,
|
EDE296531C700F4300554778 /* SignOut.swift in Sources */,
|
||||||
EDA3BE521B8B84AF00C18D70 /* SSPurchase.swift in Sources */,
|
EDA3BE521B8B84AF00C18D70 /* SSPurchase.swift in Sources */,
|
||||||
15E27926A580EABEB1B218EF /* Switch.swift in Sources */,
|
|
||||||
EDD3B3631C34709400B56B88 /* Upgrade.swift in Sources */,
|
EDD3B3631C34709400B56B88 /* Upgrade.swift in Sources */,
|
||||||
EDCBF9551D89CFC7000039C6 /* Utilities.swift in Sources */,
|
EDCBF9551D89CFC7000039C6 /* Utilities.swift in Sources */,
|
||||||
EDB6CE8C1BAEC3D400648B4D /* Version.swift in Sources */,
|
EDB6CE8C1BAEC3D400648B4D /* Version.swift in Sources */,
|
||||||
|
@ -486,19 +391,8 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
3F177C62A7053BA3ED415E5E /* Argument.swift in Sources */,
|
|
||||||
09A04CB22DC02B86AE4ACC8A /* ArgumentParser.swift in Sources */,
|
|
||||||
27340A5BB9F2A5B166E3A72A /* ArgumentProtocol.swift in Sources */,
|
|
||||||
DE6E193A6671F3D6807F746D /* Command.swift in Sources */,
|
|
||||||
4C8321353B9AE40539A1AC8A /* Errors.swift in Sources */,
|
|
||||||
F48562FA81B0C0258AC063B4 /* HelpCommand.swift in Sources */,
|
|
||||||
F86588272030FAE70093DE57 /* MASError.swift in Sources */,
|
F86588272030FAE70093DE57 /* MASError.swift in Sources */,
|
||||||
F865880B2030F6DE0093DE57 /* MASErrorTestCase.swift in Sources */,
|
F865880B2030F6DE0093DE57 /* MASErrorTestCase.swift in Sources */,
|
||||||
F6D2058A70757D3477185A50 /* Option.swift in Sources */,
|
|
||||||
073998C12AE3BEEC41CE0DAD /* OrderedSet.swift in Sources */,
|
|
||||||
C50DD25454FC5CAA1F37763F /* Result.swift in Sources */,
|
|
||||||
EC113CE6C98E8D8358228D33 /* ResultProtocol.swift in Sources */,
|
|
||||||
49C2F3FDD805256BE934A70E /* Switch.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue