@@ -11,6 +11,8 @@ import LlamaKit
1111
1212/// Represents a subcommand that can be executed with its own set of arguments.
1313public protocol CommandType {
14+ typealias ClientError
15+
1416 /// The action that users should specify to use this subcommand (e.g.,
1517 /// `help`).
1618 var verb : String { get }
@@ -20,7 +22,25 @@ public protocol CommandType {
2022 var function : String { get }
2123
2224 /// Runs this subcommand in the given mode.
23- func run( mode: CommandMode ) -> Result < ( ) , CommandantError >
25+ func run( mode: CommandMode ) -> Result < ( ) , CommandantError < ClientError > >
26+ }
27+
28+ /// A type-erased CommandType.
29+ public struct CommandOf < ClientError> : CommandType {
30+ public let verb : String
31+ public let function : String
32+ private let runClosure : CommandMode -> Result < ( ) , CommandantError < ClientError > >
33+
34+ /// Creates a command that wraps another.
35+ public init < C: CommandType where C. ClientError == ClientError > ( _ command: C ) {
36+ verb = command. verb
37+ function = command. function
38+ runClosure = { mode in command. run ( mode) }
39+ }
40+
41+ public func run( mode: CommandMode ) -> Result < ( ) , CommandantError < ClientError > > {
42+ return runClosure ( mode)
43+ }
2444}
2545
2646/// Describes the "mode" in which a command should run.
@@ -34,11 +54,11 @@ public enum CommandMode {
3454}
3555
3656/// Maintains the list of commands available to run.
37- public final class CommandRegistry {
38- private var commandsByVerb : [ String : CommandType ] = [ : ]
57+ public final class CommandRegistry < ClientError > {
58+ private var commandsByVerb : [ String : CommandOf < ClientError > ] = [ : ]
3959
4060 /// All available commands.
41- public var commands : [ CommandType ] {
61+ public var commands : [ CommandOf < ClientError > ] {
4262 return sorted ( commandsByVerb. values) { return $0. verb < $1. verb }
4363 }
4464
@@ -48,21 +68,21 @@ public final class CommandRegistry {
4868 ///
4969 /// If another command was already registered with the same `verb`, it will
5070 /// be overwritten.
51- public func register( command: CommandType ) {
52- commandsByVerb [ command. verb] = command
71+ public func register< C : CommandType where C . ClientError == ClientError > ( command: C ) {
72+ commandsByVerb [ command. verb] = CommandOf ( command)
5373 }
5474
5575 /// Runs the command corresponding to the given verb, passing it the given
5676 /// arguments.
5777 ///
5878 /// Returns the results of the execution, or nil if no such command exists.
59- public func runCommand( verb: String , arguments: [ String ] ) -> Result < ( ) , CommandantError > ? {
79+ public func runCommand( verb: String , arguments: [ String ] ) -> Result < ( ) , CommandantError < ClientError > > ? {
6080 return self [ verb] ? . run ( . Arguments( ArgumentParser ( arguments) ) )
6181 }
6282
6383 /// Returns the command matching the given verb, or nil if no such command
6484 /// is registered.
65- public subscript( verb: String ) -> CommandType ? {
85+ public subscript( verb: String ) -> CommandOf < ClientError > ? {
6686 return commandsByVerb [ verb]
6787 }
6888}
@@ -78,18 +98,18 @@ extension CommandRegistry {
7898 /// If the chosen command fails, the provided error handler will be invoked,
7999 /// then the process will exit with a failure exit code.
80100 ///
81- /// If a matching command could not be found, a helpful error message will
82- /// be written to `stderr`, then the process will exit with a failure error
83- /// code.
84- @noreturn public func main( #defaultCommand : CommandType , errorHandler: CommandantError -> ( ) ) {
101+ /// If a matching command could not be found or a usage error occurred,
102+ /// a helpful error message will be written to `stderr`, then the process
103+ /// will exit with a failure error code.
104+ @noreturn public func main( #defaultVerb : String , errorHandler: ClientError -> ( ) ) {
85105 var arguments = Process . arguments
86106 assert ( arguments. count >= 1 )
87107
88108 // Extract the executable name.
89109 let executableName = arguments. first!
90110 arguments. removeAtIndex ( 0 )
91111
92- let verb = arguments. first ?? defaultCommand . verb
112+ let verb = arguments. first ?? defaultVerb
93113 if arguments. count > 0 {
94114 // Remove the command name.
95115 arguments. removeAtIndex ( 0 )
@@ -100,7 +120,14 @@ extension CommandRegistry {
100120 exit ( EXIT_SUCCESS)
101121
102122 case let . Some( . Failure( error) ) :
103- errorHandler ( error. unbox)
123+ switch error. unbox {
124+ case let . UsageError( description) :
125+ fputs ( description + " \n " , stderr)
126+
127+ case let . CommandError( error) :
128+ errorHandler ( error. unbox)
129+ }
130+
104131 exit ( EXIT_FAILURE)
105132
106133 case . None:
0 commit comments