ArgsParser 6.0.0

dotnet add package ArgsParser --version 6.0.0                
NuGet\Install-Package ArgsParser -Version 6.0.0                
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ArgsParser" Version="6.0.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add ArgsParser --version 6.0.0                
#r "nuget: ArgsParser, 6.0.0"                
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install ArgsParser as a Cake Addin
#addin nuget:?package=ArgsParser&version=6.0.0

// Install ArgsParser as a Cake Tool
#tool nuget:?package=ArgsParser&version=6.0.0                

ArgsParser

Easy argument parsing for .Net applications. Handles options (arguments with parameters) and flags (simple switches), with the facility to show automatically generated usage details and errors.

Compatible with NetStandard 2.0 for use in Core 3 or later (tested to Net 8). Available as a nuget package.

Contents


Supported Features

  • Supports required named options
  • Supports optional named options
  • Supports defaults for option values
  • Supports optional named flags
  • Display automatically formatted help/usage text showing supported flags/options
    • Also shows argument types, defaults, and an optional legend
  • Display all errors (automatically formatted)
  • Display all provided arguments (automatically formatted, including any defaults)
  • Allows custom validators for options
    • For example enforcing that a provided string is a CSV filename
  • Option data types support any IConvertable, including int, bool, DateTime
  • Arguments can use either - or -- prefixes
  • Helpful errors for a variety of situations
    • Missing required options
    • Unknown options or flags
    • Custom validator errors
    • Option values of incorrect type
    • Unexpected values (not following an option)

Example Usage

  1. Set up the options and flags
  2. Optionally show automatically-formatted instructions to your callers
  3. Get ArgsParser to deal with the provided arguments
  4. Show any errors (also automatically formatted)
  5. Use the options and flags in your own code

There's a tiny example console app, the contents of which are basically as follows:

using ArgsParser;
...

// Define the options and flags, including whether required and any default values.
var indent = 2;
var parser = new Parser(args)
  .SupportsOption<int>("port", "Port to start the dev server on", 1337)    // Optional, with default.
  .RequiresOption<string>("read", "Folder to read the site from", "site")  // Required, with default.
  .RequiresOption<string>("write", "CSV file to write the result to")      // Required, no default.
  .SupportsFlag("serve", "Start the site going in a dev server")           // Optional flag.
  .SupportsFlag("force", "Overwrite any destination content")              // Optional flag.
  .AddCustomValidator("write", IsCSV)  // Automatic extra check.
  .ShowHelpLegend(true)  // Include explanatory notes in Help text?
  .Help(indent, "Usage:")  // Show instructions to the user.
  .Parse()  // Check the provided input arguments.
  .ShowProvided(indent, "Provided:");  // Summarise the provided options and flags.

// Show any errors, and abort.
if (parser.HasErrors)
{
    parser.ShowErrors(indent, "Issues:");
    return;
}

// Examples of accessing the options/flags.
var shouldServe = parser.IsFlagProvided("serve");
var port = parser.GetOption<int>("port");
var readFromFolder = parser.GetOption<string>("read");
var writeToFolder = parser.GetOption<string>("write");

The methods used, including AddCustomValidator(), are detailed further on.

Example Output

User command:

MyApp -serve -verbose true

When called with the above (and using the example configuration just discussed) the default values for -port and -read will be included automatically and the user will see the following:

Usage:
  -port   integer    Port to start the dev server on  [1337]
  -read   text     * Folder to read the site from  [site]
  -write  text     * Folder to write the result to
  -serve             Start the site going in a dev server
  -force             Overwrite any destination content

  * is required, values in square brackets are defaults

Provided:
  -port  1337
  -read  site
  -serve

Issues:
  -write is required
  -verbose is an unknown option

(A fuller explanation of the Issues: appears shortly in the Examples of Argument Errors section below.)

Custom Validation for Options

Flags don't need custom validation; they are either provided or they're not. For options you can add custom validation beyond the built-in 'required' setting and the user-provided value's conversion to the expected data type.

Standard validation is concerned with the presence/absence of arguments. Custom option validators allow you to also check their contents.

For example, here's a custom validator function that checks an option contains a CSV filename. This same function can be assigned to multiple options (eg both an input filename and an output filename). You can also use inline lambda but a full function is both clearer for explanatory purposes and also reusable.

/// <summary>Sample validator function which checks for a CSV filename.</summary>
/// <param name="key">Name of the argument.</param>
/// <param name="value">Content passed in.</param>
/// <returns>A list of any errors to be added to the parser's automatic ones.</returns>
private List<string> IsCSV(string key, object value)
{
    // In reality we would also need null checks etc.
    var errs = new List<string>();
    var ext = Path.GetExtension($"{value}").ToLowerInvariant();
    if (ext != ".csv") errs.Add($"{key} does not hold a CSV filename");
    return errs;
}

A custom validator always receives an option name and any value provided. It should return a list of zero or more error messages which will be automatically included alongside the standard automatically generated ones.

The incoming value is an object as the incoming data type may be one of a variety. The signature could be made generic, but validators are your code so you know what types your validator can expect and can safely cast as appropriate.

Once you have a validator you need to register it for any options requiring the check. You do this by calling AddCustomValidator with the option name and the validation function.

var parser = new Parser(args)
    .SupportsOption<string>("filename", "A CSV filename")
    .AddCustomValidator("filename", IsCSV);
parser.Parse();

Showing Helpful Information to the User

Most of the helpful text methods discussed below take two parameters:

  • int indent = 0
    • allows the lines of text to be shifted to the right
  • string heading = ""
    • any heading to show above the text (not indented)

In general an indent of 0 is fine without headings, and 2 works well with headings.

Parser.Help(int indent = 0, string heading = "");

This is the typical 'usage' information users might expect to see. Options and flags are displayed in the order they were added to the parser instance in your code, and whilst it's entirely up to you it's usually clearer if you therefore register all your options before adding any flags.

Here's an example help output showing a variety of options/flags:

-port    integer       Port to start the dev server on  [1337]
-read    text        * Folder to read the site from  [site]
-write   text        * Folder to write the result to
-apr     number        Annual interest  [3.596]
-fee     number        Monthly charge  [19.50]
-secure  true/false    Serve on HTTPS?  [True]
-until   datetime      When to stop serving  [22/08/2023 06:28:13]
-force                 Overwrite any destination content
-serve                 Start the site going in a dev server

* is required, values in square brackets are defaults

The last line is optional and can be deactivated by .ShowHelpLegend(false) when configuring the Parser instance (the default is true).

Parser.ShowErrors(int indent = 0, string heading = "")

All issues are displayed in a list. There are two distinct types of error and all errors of the first type will appear before any of the second type.

  • The first type are errors with known options/flags and they will all show first in the order the options/flags were registered in the parser
  • The second type are errors where the thing provided by the user isn't recognised (eg an unknown flag) and these appear in the order they were provided in the arguments
-write is required
-run is an unknown flag

Parser.ShowProvided(int indent = 0, string heading = "");

This shows all the provided arguments in the order the options/flags were added to the parser instance. Any options which weren't provided by the user will also appear here if a default was applied.

-port  3000
-read  in.txt
-force
-serve

Getting the Provided Options and Flags

bool IsOptionProvided(string optionName) and bool IsFlagProvided(string flagName)

These return true if the option or flag was provided.

if (parser.IsFlagProvided("serve")) ...

T GetOption<T>(string optionName)

Returns the generically typed value provided.

var port = parser.GetOption<int>("port");
  • If the option provided was the wrong type an InvalidCastException is thrown
  • If the option was not provided but there is a default configured that is returned
  • If the option was not provided and there is no default then the default value for the .Net type is returned (eg 0 for an Int32 or false for a bool)

Dictionary<string, object> Parser.GetProvided()

This is a helper method; checks for specific options and flags are simpler using the type-aware IsOptionProvided(), IsFlagProvided(), and GetOption() as detailed above. This method returns a dictionary of key/value pairs for the provided arguments in the order they were created on the parser instance in your code.

For each entry the key is the name of the matching option or flag and the value (returned as an object) contains the type-converted value for an option or null for a flag (as flags don't have values). You can easily isolate options and flags using something like .Where(x => x.Value == null).

Example usage:

// Display arguments as either `-flag` or `-option value`.
Console.Write("MyApp");
foreach (var item in parser.GetProvided())
{
    if (item.Value == null) Console.Write($" -{item.Key}");
    else Console.Write($" -{item.Key} '{item.Value}'");
}

Assuming MyApp was the name of your application, this would recreate the command used when it was called. For example:

MyApp -port "3000" -read "in.txt" -force -serve

That's a contrived example usage, though, as the next command does this for you anyway ...

string GetProvidedAsCommandArgs()

This automatically wraps up the result of Parser.GetProvided() (detailed above) as a space-delimited command argument string. In other words, it returns all the provided options/flags in the ideal format (minus the leading application name).

This can be used for example to include the command the user ran within the output their command generated.

Console.WriteLine("MyApp " + parser.GetProvidedAsCommandArgs());

This would output something like:

MyApp -port "3000" -read "in.txt" -force -serve

Checking for Errors Manually

Usually all you need to do is check one flag and use the built-in helper to show the issues:

if (parser.HasErrors)
{
  parser.ShowErrors(indent, "Issues:");
  return;
}

If you want more granular access to the errors there are two collections:

  • Dictionary<string, string> ExpectationErrors
    • Contains any errors where option/flag expectations are not met by the arguments
    • Keyed by alphabetical option/flag name
    • Example: "write", "-write does not hold a CSV filename"
  • SortedList<int, string> parser.ArgumentErrors
    • Contains errors relating to unknown arguments, indexed by order of discovery
    • Keyed by order of discovery
    • Example: 2, "-verbose is an unknown option"

Examples of Argument Errors

These examples assume the Parser was defined as shown in the Example usage section previously detailed.

Example user input:

MyApp -run data "Site Title" --serve -ignore -port 3000

There are a few things wrong with this input in relation to the setup of the options/flags in the example usage code:

  • The -write option is required but not provided
  • The provided -run option is not known
  • The "Site Title" argument is orphan text with no option name preceeding it
  • The provided -ignore flag is not known

Here's what that looks like when run:

Issues:
  -write is required
  -run is an unknown option
  Unexpected value: Site Title
  -ignore is an unknown flag

Whilst the -read option is missing there is no error logged as it was defined with a default value of site and so the requirement is automatically met. The value data is not treated as an error because even though -run is an unknown option ArgsParser knows that data would have belonged to -run so it isn't another error.

Errors come in two collections (the property Parser.HasErrors will be true if either has entries). These correspond to the two blocks of error messages described in the ShowErrors() method.

  • ExpectationErrors are where specific expectations are not met (eg a missing required option), which means the relevant option/flag whose expectations are not being met is known
    • Custom option validator errors will also be in here as they apply to known options
  • ArgumentErrors are where something was provided but the nature of the issue means we can't be sure which option/flag it relates to - for example a value was provided without an option name preceeding it

Based on the example above the errors (as key/value pairs) will be as follows:

  • ExpectationErrors keyed by the name of the related option/flag
    • writeOption missing: write
  • ArgumentErrors keyed by the 0-based offset within the arguments provided
    • 0Unknown option: run
      • This is assumed to be an option not a flag as it is followed by a value
    • 2Unexpected value: Site Title
      • This is unexpected because run was not recognised as a valid option
    • 4Unknown flag: ignore
      • This is assumed to be a flag because it is followed by -port rather than a value

Copyright K Cartlidge 2020-2024.

Licensed under GNU AGPLv3 (see here for more details). See the CHANGELOG for current status.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
6.0.0 104 10/19/2024
5.0.1 211 2/23/2024
5.0.0 169 8/27/2023
4.0.6 123 8/24/2023
4.0.5 112 8/22/2023
4.0.4 102 8/21/2023
4.0.3 102 8/21/2023
4.0.1 105 8/21/2023
4.0.0 112 8/21/2023
3.0.0 840 11/7/2020