There are several packages out there for parsing of command-line arguments, but not one of them fulfills everything I think such a library should cover.
What are these requirements?
- populates POCOs
- is not dependent on attributes (personally I find attributes, especially those with parameters, create a lot of noise when reading code)
- deals with verbs, like git, and nested verbs, too
- allows passing arguments by index, by long name and by short name
- produces good-looking and useful help
- possible to work with just the POCOs without any further configuration - for small in-house tools one often doesn't want to spend a lot of time with setting up these options
- good defaults, but at the same time configurable and extensible
Let's look at the simplest scenario. For more advanced examples, take a look here.
You want to model a single action and don't care too much about the names of the options or help-text, you just want to get over this argument parsing as quickly as possible.
// this is the info you want to get from the commandline arguments - you just define it as a regular POCO
public class Rectangle
{
public int X { get; set; }
public int Y { get; set; }
public int Width { get; set; }
public int Height { get; set; }
public Filling Filling { get; set; } = Filling.Solid;
}
// and in your Main you do:
static int Main(string[] args)
{
var parser = ParserFactory.Create("sometool", "somedescription");
parser.DefaultVerb<Rectangle>();
switch (parser.Parse(args))
{
case HelpResult help:
Console.WriteLine(help.Text);
return help.IsResultOfInvalidInput ? -1 : 0;
case Rectangle rectangle:
// do whatever you need to do
}
}
Now what are valid inputs for this setup? Here are a few examples, together with how they will fill the properties of the Rectangle
instance:
Argument string | Rectangle properties |
---|---|
10 11 12 13 | X:10, Y:11, Width:12, Height:13, Filling:Filling.Solid |
10 11 -h=13 -w=12 --filling=Hatched | X:10, Y:11, Width:12, Height:13, Filling:Filling.Hatched |
-x=10 -y=11 -h=13 -w=12 Hatched | X:10, Y:11, Width:12, Height:13, Filling:Filling.Hatched |
And what would be the content of the help.Text
property in the code-sample above?
sometool
somedescription
Usage:
sometool --x=<value> --y=<value> --width=<value> --height=<value> [--filling=<value>]
Required arguments:
--x, -x int
--y, -y int
--width, -w int
--height, -h int
Optional arguments:
--filling, -f None, Hatched, Solid
default: Solid
Examples:
sometool 10 20 30 40
sometool 10 20 30 40 Solid
sometool --x=10 --y=20 --width=30 --height=40 --filling=Solid
sometool -x=10 -y=20 -w=30 -h=40 -f=Solid
sometool -h=40 -f=Solid -x=10 -y=20 -w=30
long parameter names are case-sensitive
short parameter names are not case-sensitive
command names are not case-sensitive
Things to note:
- if you are fine with the default naming, you can just pop in your configuration objects and be done with it.
- if you set defaults for properties (different from the standard defaults), they are automatically assumed to be optional with the default value you set. In the example above
Rectangle.Filling
is such a case: it's automatically understood to be an optional parameter with the default valueFilling.Solid
. - the
Parse
method returns an object on which you can switch, using a language facility we've had now for a while. The types you need to handle are all your own POCOs that you have defined as verbs and the special typeHelpResult
HelpResult
is automatically returned when the arguments contain an explicit call for help, likesometool help
orsometool ?
or, if you have multiple verbs,sometool help myverb
- multiple verbs
- nested verbs
- boolean properties
- verb specfic help
- overriding the automatically detected settings (like the name of a parameter) see More Examples.
- change global configuration, for example the prefixes uses for parameter names
- change how names are generated for verbs and parameters
- change how types are formatted for the help texts
- change how example values are generated for the help texts
- pre-process arguments
- if you want to interact with the low-level parser, without any reflection, check out these tests
- last not least, you can always take a look at the demo tests
If you want to contribute, you are more than welcome. Fork the repository, make your desired changes, and create a pull request into the source repository default branch (you can read more about the fork and pull model here)
The solution requires .NET 6.x SDK installed on your machine.
This solution uses Paket, a dependency manager for .NET projects. To get started with the solution, run:
dotnet tool restore
dotnet paket restore
This will restore all necessary packages in order to run the solution.
Check out the issues page. There are a few issues marked as good-first-issue
and a few others as help-wanted
.
Furthermore, issues marked as needs-design
would benefit from discussion about how they should ideally work, both for end-users and for library-users.
Last not least, there are a few ideas marked as do-people-need-this
- for these, it would be very interesting to get as many opinions as possible.
The license is Creative Commons BY-SA 4.0. In essence this means you are free to use and distribute and change this tool however you see fit, as long as you provide a link to the license and share any customizations/changes you might perform under the same license.