0001 // 0002 // Command.swift 0003 // Commandant 0004 // 0005 // Created by Justin Spahr-Summers on 2014-10-10. 0006 // Copyright (c) 2014 Carthage. All rights reserved. 0007 // 0008 0009 import Foundation 0010 import Result 0011 0012 /// Represents a subcommand that can be executed with its own set of arguments. 0013 public protocol CommandType{ 0014 0015 /// The command's options type. 0016 typealias Options
Command.swift:42 private init<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(_ command: C) {Command.swift:93 public func register<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(command: C) {HelpCommand.swift:21 public struct HelpCommand<ClientError: ErrorType>: CommandType {: OptionsType 0017 0018 typealias ClientError
Command.swift:29 func run(options: Options) -> Result<(), ClientError>Command.swift:42 private init<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(_ command: C) {Command.swift:93 public func register<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(command: C) {: ErrorType = Options.ClientError 0019 0020 /// The action that users should specify to use this subcommand (e.g., 0021 /// `help`). 0022 var verb
Command.swift:42 private init<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(_ command: C) {Command.swift:93 public func register<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(command: C) {: String { get } 0023 0024 /// A human-readable, high-level description of what this command is used 0025 /// for. 0026 var function
Command.swift:43 verb = command.verbCommand.swift:94 commandsByVerb[command.verb] = CommandWrapper(command): String { get } 0027 0028 /// Runs this subcommand with the given options. 0029 func run(options: Options) -> Result<(), ClientError> 0030 } 0031 0032 /// A type-erased command. 0033 public struct CommandWrapper
Command.swift:44 function = command.function<ClientError
Command.swift:80 private var commandsByVerb: [String: CommandWrapper<ClientError>] = [:]Command.swift:83 public var commands: [CommandWrapper<ClientError>] {Command.swift:94 commandsByVerb[command.verb] = CommandWrapper(command): ErrorType> { 0034 public let verb
Command.swift:39 public let usage: () -> CommandantError<ClientError>?Command.swift:42 private init<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(_ command: C) {Command.swift:42 private init<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(_ command: C) {Command.swift:62 usage = { () -> CommandantError<ClientError>? in: String 0035 public let function
Command.swift:43 verb = command.verbCommand.swift:84 return commandsByVerb.values.sort { return $0.verb < $1.verb }Command.swift:84 return commandsByVerb.values.sort { return $0.verb < $1.verb }HelpCommand.swift:50 let maxVerbLength = self.registry.commands.map { $0.verb.characters.count }.maxElement() ?? 0HelpCommand.swift:53 let padding = Repeat<Character>(count: maxVerbLength - command.verb.characters.count, repeatedValue: " ")HelpCommand.swift:54 print(" \(command.verb)\(String(padding)) \(command.function)"): String 0036 0037 public let run: ArgumentParser -> Result<(), CommandantError<ClientError>> 0038 0039 public let usage: () -> CommandantError<ClientError>? 0040 0041 /// Creates a command that wraps another. 0042 private init
Command.swift:44 function = command.functionHelpCommand.swift:54 print(" \(command.verb)\(String(padding)) \(command.function)")<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(_ command: C) { 0043 verb = command.verb 0044 function = command.function 0045 run = { (arguments: ArgumentParser) -> Result<(), CommandantError<ClientError>> in 0046 let options = C.Options.evaluate(.Arguments(arguments)) 0047 0048 if let remainingArguments = arguments.remainingArguments { 0049 return .Failure(unrecognizedArgumentsError(remainingArguments)) 0050 } 0051 0052 switch options { 0053 case let .Success(options): 0054 return command 0055 .run(options) 0056 .mapError(CommandantError.CommandError) 0057 0058 case let .Failure(error): 0059 return .Failure(error) 0060 } 0061 } 0062 usage = { () -> CommandantError<ClientError>? in 0063 return C.Options.evaluate(.Usage).error 0064 } 0065 } 0066 } 0067 0068 /// Describes the "mode" in which a command should run. 0069 public enum CommandMode
Command.swift:94 commandsByVerb[command.verb] = CommandWrapper(command){ 0070 /// Options should be parsed from the given command-line arguments. 0071 case Arguments
Argument.swift:38 public func <| <T: ArgumentType, ClientError>(mode: CommandMode, argument: Argument<T>) -> Result<T, CommandantError<ClientError>> {Argument.swift:64 public func <| <T: ArgumentType, ClientError>(mode: CommandMode, argument: Argument<[T]>) -> Result<[T], CommandantError<ClientError>> {HelpCommand.swift:72 public static func evaluate(m: CommandMode) -> Result<HelpOptions, CommandantError<ClientError>> {Option.swift:43 static func evaluate(m: CommandMode) -> Result<Self, CommandantError<ClientError>>Option.swift:50 public static func evaluate(m: CommandMode) -> Result<NoOptions, CommandantError<ClientError>> {Option.swift:163 public func <| <T: ArgumentType, ClientError>(mode: CommandMode, option: Option<T>) -> Result<T, CommandantError<ClientError>> {Option.swift:202 public func <| <ClientError>(mode: CommandMode, option: Option<Bool>) -> Result<Bool, CommandantError<ClientError>> {Switch.swift:52 public func <| <ClientError> (mode: CommandMode, option: Switch) -> Result<Bool, CommandantError<ClientError>> {(ArgumentParser) 0072 0073 /// Each option should record its usage information in an error, for 0074 /// presentation to the user. 0075 case Usage
Argument.swift:40 case let .Arguments(arguments):Argument.swift:66 case let .Arguments(arguments):Option.swift:165 case let .Arguments(arguments):Option.swift:204 case let .Arguments(arguments):Switch.swift:54 case let .Arguments(arguments):0076 } 0077 0078 /// Maintains the list of commands available to run. 0079 public final class CommandRegistry
Argument.swift:55 case .Usage:Argument.swift:93 case .Usage:Option.swift:193 case .Usage:Option.swift:213 case .Usage:Switch.swift:61 case .Usage:<ClientError
Command.swift:112 extension CommandRegistry {HelpCommand.swift:27 private let registry: CommandRegistry<ClientError>HelpCommand.swift:31 public init(registry: CommandRegistry<ClientError>) {: ErrorType> { 0080 private var commandsByVerb
Command.swift:80 private var commandsByVerb: [String: CommandWrapper<ClientError>] = [:]Command.swift:83 public var commands: [CommandWrapper<ClientError>] {Command.swift:93 public func register<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(command: C) {Command.swift:93 public func register<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(command: C) {: [String: CommandWrapper<ClientError>] = [:] 0081 0082 /// All available commands. 0083 public var commands
Command.swift:84 return commandsByVerb.values.sort { return $0.verb < $1.verb }Command.swift:94 commandsByVerb[command.verb] = CommandWrapper(command)Command.swift:108 return commandsByVerb[verb]: [CommandWrapper<ClientError>] { 0084 return commandsByVerb.values.sort { return $0.verb < $1.verb } 0085 } 0086 0087 public init() {} 0088 0089 /// Registers the given command, making it available to run. 0090 /// 0091 /// If another command was already registered with the same `verb`, it will 0092 /// be overwritten. 0093 public func register<C: CommandType where C.ClientError == ClientError, C.Options.ClientError == ClientError>(command: C) { 0094 commandsByVerb[command.verb] = CommandWrapper(command) 0095 } 0096 0097 /// Runs the command corresponding to the given verb, passing it the given 0098 /// arguments. 0099 /// 0100 /// Returns the results of the execution, or nil if no such command exists. 0101 public func runCommand(verb: String, arguments: [String]) -> Result<(), CommandantError<ClientError>>? { 0102 return self[verb]?.run(ArgumentParser(arguments)) 0103 } 0104 0105 /// Returns the command matching the given verb, or nil if no such command 0106 /// is registered. 0107 public subscript(verb: String) -> CommandWrapper<ClientError>? { 0108 return commandsByVerb[verb] 0109 } 0110 } 0111 0112 extension CommandRegistry { 0113 /// Hands off execution to the CommandRegistry, by parsing Process.arguments 0114 /// and then running whichever command has been identified in the argument 0115 /// list. 0116 /// 0117 /// If the chosen command executes successfully, the process will exit with 0118 /// a successful exit code. 0119 /// 0120 /// If the chosen command fails, the provided error handler will be invoked, 0121 /// then the process will exit with a failure exit code. 0122 /// 0123 /// If a matching command could not be found but there is any `executable-verb` 0124 /// style subcommand executable in the caller's `$PATH`, the subcommand will 0125 /// be executed. 0126 /// 0127 /// If a matching command could not be found or a usage error occurred, 0128 /// a helpful error message will be written to `stderr`, then the process 0129 /// will exit with a failure error code. 0130 @noreturn public func main(defaultVerb defaultVerb: String, errorHandler: ClientError -> ()) { 0131 main(arguments: Process.arguments, defaultVerb: defaultVerb, errorHandler: errorHandler) 0132 } 0133 0134 /// Hands off execution to the CommandRegistry, by parsing `arguments` 0135 /// and then running whichever command has been identified in the argument 0136 /// list. 0137 /// 0138 /// If the chosen command executes successfully, the process will exit with 0139 /// a successful exit code. 0140 /// 0141 /// If the chosen command fails, the provided error handler will be invoked, 0142 /// then the process will exit with a failure exit code. 0143 /// 0144 /// If a matching command could not be found but there is any `executable-verb` 0145 /// style subcommand executable in the caller's `$PATH`, the subcommand will 0146 /// be executed. 0147 /// 0148 /// If a matching command could not be found or a usage error occurred, 0149 /// a helpful error message will be written to `stderr`, then the process 0150 /// will exit with a failure error code. 0151 @noreturn public func main
HelpCommand.swift:50 let maxVerbLength = self.registry.commands.map { $0.verb.characters.count }.maxElement() ?? 0HelpCommand.swift:52 for command in self.registry.commands {(arguments arguments: [String], defaultVerb: String, errorHandler: ClientError -> ()) { 0152 assert(arguments.count >= 1) 0153 0154 var arguments = arguments 0155 0156 // Extract the executable name. 0157 let executableName = arguments.removeAtIndex(0) 0158 0159 let verb = arguments.first ?? defaultVerb 0160 if arguments.count > 0 { 0161 // Remove the command name. 0162 arguments.removeAtIndex(0) 0163 } 0164 0165 switch runCommand(verb, arguments: arguments) { 0166 case .Success?: 0167 exit(EXIT_SUCCESS) 0168 0169 case let .Failure(error)?: 0170 switch error { 0171 case let .UsageError(description): 0172 fputs(description + "\n", stderr) 0173 0174 case let .CommandError(error): 0175 errorHandler(error) 0176 } 0177 0178 exit(EXIT_FAILURE) 0179 0180 case nil: 0181 if let subcommandExecuted = executeSubcommandIfExists(executableName, verb: verb, arguments: arguments) { 0182 exit(subcommandExecuted) 0183 } 0184 0185 fputs("Unrecognized command: '\(verb)'. See `\(executableName) help`.\n", stderr) 0186 exit(EXIT_FAILURE) 0187 } 0188 } 0189 0190 /// Finds and executes a subcommand which exists in your $PATH. The executable 0191 /// name must be in the form of `executable-verb`. 0192 /// 0193 /// - Returns: The exit status of found subcommand or nil. 0194 private func executeSubcommandIfExists
Command.swift:131 main(arguments: Process.arguments, defaultVerb: defaultVerb, errorHandler: errorHandler)(executableName: String, verb: String, arguments: [String]) -> Int32? { 0195 let subcommand = "\((executableName as NSString).lastPathComponent)-\(verb)" 0196 0197 func launchTask(path: String, arguments: [String]) -> Int32 { 0198 let task = NSTask() 0199 task.launchPath = path 0200 task.arguments = arguments 0201 0202 task.launch() 0203 task.waitUntilExit() 0204 0205 return task.terminationStatus 0206 } 0207 0208 guard launchTask("/usr/bin/which", arguments: [ "-s", subcommand ]) == 0 else { 0209 return nil 0210 } 0211 0212 return launchTask("/usr/bin/env", arguments: [ subcommand ] + arguments) 0213 } 0214 } 0215
Command.swift:181 if let subcommandExecuted = executeSubcommandIfExists(executableName, verb: verb, arguments: arguments) {