Manual Parsing and Testing
Provide your own array of command-line inputs or work directly with parsed command-line arguments.
ManualParsing.mdOverview
For most programs, denoting the root command type as @main
is all that’s necessary. As the program’s entry point, that type parses the command-line arguments to find the correct command from your tree of nested subcommands, instantiates and validates the result, and executes the chosen command. For more control, however, you can perform each of those steps manually.
Parsing Arguments
For simple Swift scripts, and for those who prefer a straight-down-the-left-edge-of-the-screen scripting style, you can define a single ParsableArguments
type to parse explicitly from the command-line arguments.
Let’s implement the Select
command discussed in Providing Custom Validation, but using a scripty style instead of the typical command. First, we define the options as a ParsableArguments
type:
struct SelectOptions: ParsableArguments {
@Option var count: Int = 1
@Argument var elements: [String] = []
}
The next step is to parse our options from the command-line input:
let options = SelectOptions.parseOrExit()
The static parseOrExit(_:)
method either returns a fully initialized instance of the type, or exits with an error message and code. Alternatively, you can call the throwing parse(_:)
method if you’d like to catch any errors that arise during parsing.
We can perform validation on the inputs and exit the script if necessary:
guard options.elements.count >= options.count else {
let error = ValidationError("Please specify a 'count' less than the number of elements.")
SelectOptions.exit(withError: error)
}
As you would expect, the exit(withError:)
method includes usage information when you pass it a ValidationError
.
Finally, we print out the requested number of elements:
let chosen = options.elements
.shuffled()
.prefix(options.count)
print(chosen.joined(separator: "\n"))
Parsing Commands
Manually parsing commands is a little more complex than parsing a simple ParsableArguments
type. The result of parsing from a tree of subcommands may be of a different type than the root of the tree, so the static parseAsRoot(_:)
method returns a type-erased ParsableCommand
.
Let’s see how this works by using the Math
command and subcommands defined in Defining Commands and Subcommands. This time, instead of calling Math.main()
, we’ll call Math.parseAsRoot()
, and switch over the result:
do {
var command = try Math.parseAsRoot()
switch command {
case var command as Math.Add:
print("You chose to add \(command.options.values.count) values.")
command.run()
default:
print("You chose to do something else.")
try command.run()
}
} catch {
Math.exit(withError: error)
}
Our new logic intercepts the command between validation and running, and outputs an additional message:
% math 10 15 7
You chose to add 3 values.
32
% math multiply 10 15 7
You chose to do something else.
1050
Providing Command-Line Input
All of the parsing methods — parse()
, parseOrExit()
, and parseAsRoot()
— can optionally take an array of command-line inputs as an argument. You can use this capability to test your commands, to perform pre-parse filtering of the command-line arguments, or to manually execute commands from within the same or another target.
Let’s update our select
script above to strip out any words that contain all capital letters before parsing the inputs.
let noShoutingArguments = CommandLine.arguments.dropFirst().filter { phrase in
phrase.uppercased() != phrase
}
let options = SelectOptions.parseOrExit(noShoutingArguments)
Now when we call our command, the parser won’t even see the capitalized words — HEY
won’t ever be printed:
% select hi howdy HEY --count 2
hi
howdy
% select hi howdy HEY --count 2
howdy
hi
See also
Experimental Features
Learn about ArgumentParser’s experimental features.
Read More