LunarDoggo.StartOptions 1.1.0-preview.20220622

This is a prerelease version of LunarDoggo.StartOptions.
There is a newer version of this package available.
See the version list below for details.
dotnet add package LunarDoggo.StartOptions --version 1.1.0-preview.20220622                
NuGet\Install-Package LunarDoggo.StartOptions -Version 1.1.0-preview.20220622                
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="LunarDoggo.StartOptions" Version="1.1.0-preview.20220622" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add LunarDoggo.StartOptions --version 1.1.0-preview.20220622                
#r "nuget: LunarDoggo.StartOptions, 1.1.0-preview.20220622"                
#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 LunarDoggo.StartOptions as a Cake Addin
#addin nuget:?package=LunarDoggo.StartOptions&version=1.1.0-preview.20220622&prerelease

// Install LunarDoggo.StartOptions as a Cake Tool
#tool nuget:?package=LunarDoggo.StartOptions&version=1.1.0-preview.20220622&prerelease                

StartOptions

Library for parsing commandline start options for .net and .net-core applications (.net-standard 1.3)

Terminology

A StartOption is a single commandline argument that may contain values. StartOptions can either be grouped by StartOptionGroups or be groupless and can therefore be used for global flags, such as verbose or debug.

A StartOptionGroup is a grouping of StartOptions, it has a specific name associated with it. The StartOptionGroup commandline argument always is of type Switch, it additionally requires at least one subordinate StartOption to be defined. Also note that you can only use one StartOptionGroup at once when calling your application.

A StartOptionParser takes the commandline arguments and parses them into an optional StartOptionGroup and StartOptions which you can use to determine the inputs from the cli. If you use either of the two approaches mentioned in the section Getting started you don't need to use this class.

AbstractApplication is a base type that creates a StartOptionParser, passes your application's commandline arguments to it and handles its output using the builder-approach mentioned below. You must override the following methods:

//Prints your help-page if the commandline arguments containes a HelpOption
protected abstract void PrintHelpPage(StartOptionParserSettings settings, IEnumerable<HelpOption> helpOptions, IEnumerable<StartOptionGroup> groups, IEnumerable<StartOption> grouplessOptions);

//Runs your application code with the parsed StartOptionGroup and groupless StartOptions
protected abstract void Run(StartOptionGroup selectedGroup, IEnumerable<StartOption> selectedGrouplessOptions);

//Gets the ApplicationStartOptions for the StartOptionParser-creation
protected abstract ApplicationStartOptions GetApplicationStartOptions();

CommandApplication is a base type that does the same thing as AbstractApplication, but with the attribute-based approach mentioned below. You must override the following methods:

//Prints your help-page if the commandline arguments containes a HelpOption
protected abstract void PrintHelpPage(StartOptionParserSettings settings, IEnumerable<HelpOption> helpOptions, IEnumerable<StartOptionGroup> groups, IEnumerable<StartOption> grouplessOptions);

//Get the types of all commands your appllication supports
protected abstract Type[] GetCommandTypes();

ApplicationStartOptions is a container used by an AbstractApplication or a CommandApplication in order to provide the StartOptionParser with its parameters. It is just a simple container for StartOptionGroups, groupless StartOptions, HelpOptions and StartOptionParserSettings


Getting started

The first step to using this library is to install the lates version of the nuget package for your project, for example by using the package manager console in Visual Studio:

Install-Package LunarDoggo.StartOptions [-ProjectName <your project name>]

This library provides two distinct ways for customizing your application's command line arguments:

  • by utilizing attributes
  • by building the StartOptions yourself

1. Using the attribute-based approach

If you chose the attribute-based approach, first create some classes that implement the interface IApplicationCommand, make sure to define at least one constructor per class that is decorated with the StartOptionGroupAttribute and contains parameters that are decorated with the StartOptionAttribute. If you want to add constructor parameters that aren't decorated with a StartOptionAttribute, you have to provide an IDependencyProvider. The method Execute() contains the code that will run the command:

public class AddCommand : IApplicationCommand
{
    private readonly int firstValue, secondValue;

    [StartOptionGroup("add", "a", Description = "Adds two integers together")]
    public AddCommand([StartOption("value-1", "1", Description = "First value", Mandatory = true, ValueType = StartOptionValueType.Single, ParserType = typeof(Int32OptionValueParser))] int firstValue,
                        [StartOption("value-2", "2", Description = "Second value", Mandatory = true, ValueType = StartOptionValueType.Single, ParserType = typeof(Int32OptionValueParser))] int secondValue)
    {
        this.secondValue = secondValue;
        this.firstValue = firstValue;
    }

    public void Execute()
    {
        Console.WriteLine("{0} + {1} = {2}", this.firstValue, this.secondValue, this.firstValue + this.secondValue);
    }
}

Note: See Final notes and hints below to learn how to implement groupless StartOptions.

After you created all the commands you want to use, create a new class that inherits from CommandApplication and override its abstract methods:

class DemoApplication : CommandApplication

Set the commands the application will be able to run, all available command line arguments will be extracted from:

protected override Type[] GetCommandTypes()
{
    return new[] { typeof(AddCommand), typeof(ReadFileCommand) };
}

Set the IDependencyProvider to be used to resolve constructor parameters that aren't decorated with a StartOptionAttribute. If you don't want to use this feature, simply return null. The library is shipped with the SimpleDependencyProvider, you can also use dependency injection frameworks, like Ninject, but you'll have to implement a proxy class implementing IDependencyProvider to allow access to the dependency framework. This example will use the SimpleDependencyProvider:

protected override IDependencyProvider GetDependencyProvider()
{
    //true: throw an exception if a dependency can't be resolved
    SimpleDependencyProvider provider = new SimpleDependencyProvider(true);
    //Add dependencies to the cache like so, keep in mind that every Type can only be registered once:
    provider.AddSingleton<IDatabase>(new MySqlDatabase("connection string here"));
    return provider;
}

And finally implement the method that will print the help page of your application, you can implement a custom help page printing mechanism or simply use the included class ConsoleHelpPrinter:

protected override void PrintHelpPage(StartOptionParserSettings settings, IEnumerable<HelpOption> helpOptions, IEnumerable<StartOptionGroup> groups, IEnumerable<StartOption> grouplessOptions)
{
    new ConsoleHelpPrinter('\t').Print(settings, helpOptions, groups, grouplessOptions);
}

2. Using StartOptionGroup- and StartOption-builders

If you picked the builder-based approach, create a new class that inherits from AbstractApplication and override its abstract methods:

class DemoApplication : AbstractApplication

Set your ApplicationStartOptions inside of GetApplicationStartOptions, note that valid StartOption names must start with either a letter or a number and can only contain letters, numbers, underscores and hyphens/dashes. Also note, that you can also provide custom HelpOptions and StartOptionParserSettings in this method (see Demo-Project):

protected override ApplicationStartOptions GetApplicationStartOptions()
{
    StartOptionGroup[] groups = new StartOptionGroup[]
    {
        new StartOptionGroupBuilder("add", "a").SetDescription("Adds two integers together")
                .AddOption("value-1", "1", (_option) => _option.SetDescription("First value").SetValueType(StartOptionValueType.Single).SetValueParser(new Int32OptionValueParser()).SetRequired())
                .AddOption("value-2", "2", (_option) => _option.SetDescription("Second value").SetValueType(StartOptionValueType.Single).SetValueParser(new Int32OptionValueParser()).SetRequired())
                .Build()
    };
    
    StartOption[] grouplessOptions = new StartOption[]
    {
        new StartOptionBuilder("verbose", "v").SetDescription("Enable verbose output").Build()
    };
    
    return new ApplicationStartOptions(groups, grouplessOptions);
}

Set the logic for printing help-pages inside of PrintHelpPage. You can use the predefined class ConsoleHelpPrinter or define your own method of displaying a help page to your user:

protected override void PrintHelpPage(StartOptionParserSettings settings, IEnumerable<HelpOption> helpOptions, IEnumerable<StartOptionGroup> groups, IEnumerable<StartOption> grouplessOptions)
{
    new ConsoleHelpPrinter('\t').Print(settings, helpOptions, groups, grouplessOptions);
}

Set the logic of the execution of your application inside of Run:

protected override void Run(StartOptionGroup selectedGroup, IEnumerable<StartOption> selectedGrouplessOptions)
{
    if(selectedGrouplessOptions.Any(_option => _option.ShortName.Equals("v")))
    {
        Console.WriteLine("Verbose option was toggled");
    }

    switch(selectedGroup.ShortName)
    {
        case "a":
            StartOption firstOption = group.GetOptionByShortName("1");
            StartOption secondOption = group.GetOptionByShortName("2");

            if (firstOption.HasValue && secondOption.HasValue)
            {
                int first = firstOption.GetValue<int>();
                int second = secondOption.GetValue<int>();

                Console.WriteLine("{0} + {1} = {2}", first, second, first + second);
            }
            else
            {
                if (!firstOption.HasValue)
                {
                    Console.WriteLine("Please provide the first number for the addition");
                }
                if(!secondOption.HasValue)
                {
                    Console.WriteLine("Please provide the second number for the addition");
                }
            }
            break;
        default: throw new NotImplementedException();
    }
}

Regardless of whether you chose the attribute-based or builder-based approach, you must finally instantiate your application class in the Main(string[] args)-method and call the Run(string[] args)-method on your application class:

static void Main(string[] args)
{
    DemoApplication application = new DemoApplication();
    application.Run(args);
}

Afterwards you will be able to call your application from the commandline like so:

/> .\DemoApplication.exe --add -1=10 -2=5 --verbose -h

In this example, your application will be using the "add"-StartOptionGroup with the subordinate StartOptions "value-1" (value = 10) and "value-2" (value = 5), the verbose-flag is set. As the HelpOption "h" is also used, your application will display the help-page for the provided commandline arguments without actually executing the operation, in order to run the addition, just omit the "-h" option.


Final notes and hints

  1. Please note, that the help printer by default only displays all options if your commandline arguments only contained HelpOptions if they contained StartOptions or StartOptionGroups, the help page will only contain descriptions to the provided options. To change this behaviour, override the following Method in your Application-class:
protected override void PrintHelpPage(ParsedStartOptions parsed)
{
    //The following four lines assume, that you defined methods for getting your StartOptionParserSettings, StartOptionGroups, StartOptions and HelpOptions
    StartOptionParserSettings settings = this.GetStartOptionParserSettings();
    IEnumerable<StartOptionGroup> groups = this.GetStartOptionGroups();
    IEnumerable<HelpOption> helpOptions = this.getHelpOptions();
    IEnumerable<StartOption> options = this.GetStartOptions();

    this.PrintHelpPage(settings, helpOptions, groups, options);
}
  1. If you wish to parse command line arguments in the command-based approach that are not of type string, you must specify the parser a StartOption should use to parse its values:
[StartOptionGroup("add", "a", Description = "Adds two integers together")]
public AddCommand([StartOption("value-1", "1", Description = "First value", Mandatory = true, ValueType = StartOptionValueType.Single, ParserType = typeof(Int32OptionValueParser))] int firstValue,
                    [StartOption("value-2", "2", Description = "Second value", Mandatory = true, ValueType = StartOptionValueType.Single, ParserType = typeof(Int32OptionValueParser))] int secondValue)
{
    this.secondValue = secondValue;
    this.firstValue = firstValue;
}
  1. If you wish to implement a custom IStartOptionValueParser with the command-based approach you must register your custom parser in the StartOptionValueParserRegistry before using it:
class CustomOptionValueParser : AbstractStartOptionValueParser
{
    ...
}

static void Main(string[] args)
{
    StartOptionValueParserRegistry.Register(new CustomOptionValueParser());
    DemoApplication application = new DemoApplication();
    application.Run(args);
}
  1. If you wish to use the same groupless StartOption with the command-based approach across multiple different constructors or classes, at first implement one command with a constructor parameter decorated with GrouplessStartOptionAttribute and another one (another constructor in the same or a different class) decorated with GrouplessStartOptionReferenceAttribute. Make sure that no groupless start options use duplicate names:
class FirstCommand : ApplicationCommand
{
    [StartOptionGroup("first", "f")]
    public FirstCommand(..., [GrouplessStartOption("verbose", "v", Description = "Enable verbose output", IsGrouplessOption = true)]bool verbose)
    {
        ...
    }
    ...
}

class SecondCommand : ApplicationCommand
{
    [StartOptionGroup("second", "s")]
    public SecondCommand(..., [GrouplessStartOptionReference("verbose")]bool verbose)
    {
        ...
    }
    ...
}
  1. 1 You can also decorate your CommandApplication or any of your IApplicationCommand implementations with GrouplessStartOptionAttribute if you prefer your grouless StartOptions to be declared in a central location:
[GrouplessStartOption("verbose", "v", Description = "Enable verbose output")]
public class DemoApplication : CommandApplication
{
    ...
}

[GrouplessStartOption("debug", "d", Description = "Enable debug mode")]
public class DemoCommand : IApplicationCommand
{
    public DemoCommand(..., [GrouplessStartOptionReference("verbose")]bool verbose,
                       [GrouplessStartOptionReference("debug")]bool debug)
   {
       ...
   }
    ...
}
  1. If you want to handle groupless StartOptions of your CommandApplication in a central location, you can attach handlers to your application (note: right now you can only attach one handler per groupless StartOption) by using these methods either in your application's constructor or from outside your application class:
CommandApplication app = new DemoApplication();
//for groupless StartOptions of type Multiple
app.AddGlobalGrouplessStartOptionHandler<T>("numbers", (int[] _numbers) => this.Numbers = _numbers);
//for groupless StartOptions of type Single
app.AddGlobalGrouplessStartOptionHandler<T>("user", (string _username) => this.Username = _username);
//for groupless StartOptions of type Switch
app.AddGlobalGrouplessStartOptionHandler("verbose", () => this.Verbose = true);
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 netcoreapp1.0 was computed.  netcoreapp1.1 was computed.  netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard1.3 is compatible.  netstandard1.4 was computed.  netstandard1.5 was computed.  netstandard1.6 was computed.  netstandard2.0 was computed.  netstandard2.1 was computed. 
.NET Framework net46 was computed.  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 tizen30 was computed.  tizen40 was computed.  tizen60 was computed. 
Universal Windows Platform uap was computed.  uap10.0 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.

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
1.1.0 504 6/30/2022
1.1.0-preview.20220622 123 6/22/2022
1.1.0-preview.20220605 140 6/5/2022
1.1.0-preview.20220513 144 5/13/2022
1.1.0-preview.20220417 134 4/17/2022
1.1.0-preview.20220411 137 4/11/2022
1.1.0-preview.20220410 142 4/10/2022
1.0.2 478 4/1/2022
1.0.1 447 4/1/2022
1.0.0 632 7/12/2020

Added global groupless start option handling for CommandApplications, GrouplessStartOptionAttribute can now decorate CommandApplications; note: the new parts of the library may contain undiscovered major bugs