This PR introduces a new API on the Docopt class with the following benefits:
It has a static entry-point therefore does not need an instance of Docopt with any setup.
Leaves the user/caller with choices about how to display help, version, errors and handling arguments.
Errors in command-line usage return an input error result instead of throwing an exception.
Returns each argument value as the more efficient Value instead of ValueObject.
Provides strong-typed and compile-time guarantees based on choices like needing support for greedy version and help option parsing.
Co-exists with the previous instance-based API therefore there are no breaking changes being introduced.
The new API introduces the notion of a strong-typed parser that produces 4 distinct results:
IArgumentsResult<T>: the parsed arguments as T when command-line is valid per usage
IHelpResult: the result when help is requested via -h or --help
IVersionResult: the result when version is requested via --version
IInputErrorResult: the input error result if the given command-line arguments do not match the usage
There is also an API for creating and configuring a parser given a docopt text source. Depending on the configuration, the parser changes its API shape to reflect the results it will produce (one of the four above). Consequently, there are also four parser types:
IBaselineParser<T>: a parser that either produces the parsed arguments or an input error result on incorrect usage.
IHelpFeaturingParser<T>: like IBaselineParser<T> but will also produce a help result if the help option is present.
IVersionFeaturingParser<T>: like IBaselineParser<T> but will also produce a version result if the version option is present.
IParser<T>: a parser that produces any of the four possible results.
The table below summarizes the possible results produced by each parser when the Parse method of a parser is called:
Parser/Result
IArgumentsResult<T>
IInputErrorResult
IHelpResult
IVersionResult
IBaselineParser<T>
✅
✅
IHelpFeaturingParser<T>
✅
✅
✅
IVersionFeaturingParser<T>
✅
✅
✅
IParser<T>
✅
✅
✅
✅
There is a new static method on Docopt called CreateParser:
public static IHelpFeaturingParser<IDictionary<string, Value>> CreateParser(string doc)
Below is a demonstration of how it is designed to be used, taking NavalFate as an example:
using System;
using System.Collections.Generic;
using DocoptNet;
const string help = @"Naval Fate.
Usage:
naval_fate.exe ship new <name>...
naval_fate.exe ship <name> move <x> <y> [--speed=<kn>]
naval_fate.exe ship shoot <x> <y>
naval_fate.exe mine (set|remove) <x> <y> [--moored | --drifting]
naval_fate.exe (-h | --help)
naval_fate.exe --version
Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.
";
var argsParser = Docopt.CreateParser(help).WithVersion("Naval Fate 2.0");
Once the parser is initialized, it's Parse method can be given the command-line arguments. The result of the parsing can be done in one of two ways. Either using pattern-matching:
switch (argsParser.Parse(args))
{
case IArgumentsResult<IDictionary<string, Value>> { Arguments: var arguments }:
foreach (var (key, value) in arguments)
Console.WriteLine("{0} = {1}", key, value);
return 0;
case IHelpResult:
Console.WriteLine(help);
return 0;
case IVersionResult { Version: var version }:
Console.WriteLine(version);
return 0;
case IInputErrorResult { Usage: var usage }:
Console.Error.WriteLine(usage);
return 1;
case var result:
throw new System.Runtime.CompilerServices.SwitchExpressionException(result);
}
or using the Match method of the parse result like so:
return argsParser.Parse(args)
.Match(Run,
result => { Console.WriteLine(result.Help); return 0; },
result => { Console.WriteLine(result.Version); return 0; },
result => { Console.Error.WriteLine(result); return 1; });
static int Run(IDictionary<string, Value> arguments)
{
foreach (var (key, value) in arguments)
Console.WriteLine("{0} = {1}", key, value);
return 0;
}
The second approach provides strong compile-time guarantees. For example, it the earlier call to setup the parser is changed to read instead:
var argsParser = Docopt.CreateParser(help)/*.WithVersion("Naval Fate 2.0")*/;
then the version using pattern-matching will still compile, but the Match will produce a compile-time error since its arity will change and the code will need to be updated to compile again:
return argsParser.Parse(args)
.Match(Run,
result => { Console.WriteLine(result.Help); return 0; },
// result => { Console.WriteLine(result.Version); return 0; },
result => { Console.Error.WriteLine(result); return 1; });
See also tests/DocoptNet.Tests/ParserApiTests.cs for example uses.
The source generator has also been update to use this new API.
This PR introduces a new API on the
Docopt
class with the following benefits:Docopt
with any setup.Value
instead ofValueObject
.The new API introduces the notion of a strong-typed parser that produces 4 distinct results:
IArgumentsResult<T>
: the parsed arguments asT
when command-line is valid per usageIHelpResult
: the result when help is requested via-h
or--help
IVersionResult
: the result when version is requested via--version
IInputErrorResult
: the input error result if the given command-line arguments do not match the usageThere is also an API for creating and configuring a parser given a docopt text source. Depending on the configuration, the parser changes its API shape to reflect the results it will produce (one of the four above). Consequently, there are also four parser types:
IBaselineParser<T>
: a parser that either produces the parsed arguments or an input error result on incorrect usage.IHelpFeaturingParser<T>
: likeIBaselineParser<T>
but will also produce a help result if the help option is present.IVersionFeaturingParser<T>
: likeIBaselineParser<T>
but will also produce a version result if the version option is present.IParser<T>
: a parser that produces any of the four possible results.The table below summarizes the possible results produced by each parser when the
Parse
method of a parser is called:IArgumentsResult<T>
IInputErrorResult
IHelpResult
IVersionResult
IBaselineParser<T>
IHelpFeaturingParser<T>
IVersionFeaturingParser<T>
IParser<T>
There is a new static method on
Docopt
calledCreateParser
:Below is a demonstration of how it is designed to be used, taking NavalFate as an example:
Once the parser is initialized, it's
Parse
method can be given the command-line arguments. The result of the parsing can be done in one of two ways. Either using pattern-matching:or using the
Match
method of the parse result like so:The second approach provides strong compile-time guarantees. For example, it the earlier call to setup the parser is changed to read instead:
then the version using pattern-matching will still compile, but the
Match
will produce a compile-time error since its arity will change and the code will need to be updated to compile again:See also
tests/DocoptNet.Tests/ParserApiTests.cs
for example uses.The source generator has also been update to use this new API.