Console.Routing 3.1.0

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

// Install Console.Routing as a Cake Tool
#tool nuget:?package=Console.Routing&version=3.1.0

Console.Routing

Console.Routing is a framework that makes it easy to build command line tools. Proper command line parsing can be tricky and time consuming. This framework hopes to take that away. With this library You can implement a command line tool with almost no overhead.

Console.Routing works with a router that somewhat similar to ASP.NET. It routes commands and parameters from the command line a specific C# method and fills in all method parameters with the matching command line arguments. If no route is found, a clear user friendly error is given.

Documentation

Setup

By adding one line of code to the Program.Main you enable route discovery and handling of arguments.

 using ConsoleRouting;
 
 Routing.Handle(args);

The examples below describe commands for a fictitious tool named tool.

Discovery

You can expose any method to command line argument with the [Command] attribute. To avoid accidental exposure, the declaring class must be marked with [Module]. This example is the bare minimum to get your first command up-and-running.

[Module]
public class MyTool
{
    [Command]
    public void Hello()
    {
        Console.WriteLine("Hello world!");
    }
}

Which can be executed on the command line with:

> tool hello

Parameter types

String parameters

To interpret a command line parameter as a string, you can add a string parameter to your command method.

> tool hello John
    [Command]
    public void Hello(string name)
    {
        Console.Writeline("Hello " + name);
    }
	

Flag parameters

Flags are used to set a setting to true. Each of the following three command lines statements will route to this method:

> tool hello John 
> tool hello John --upper
    [Command]
    public void Hello(string name, Flag upper)
    {
        if (upper) name = name.ToUpper();
        Console.Writeline("Hello " + name);
    }

The Flag has a default cast to bool.

Alternatively, you can also use a bool parameter. The following method has the same command line signature as above.

    [Command]
    public void Hello(string name, bool upper)
    {
        if (upper) name = name.ToUpper();
        Console.Writeline("Hello " + name);
    }

Aligning with linux style arguments, you can abbreviate flags by using a single dash, and the first letter.

> tool hello John -u

You can group multiple letter flags:

> tool feed -cdr
   
    [Command]
    public void Feed(Flag cat, Flag dog, Flag rabbit)
    {
    
    }

Flag parameters with a value

In some tools it's common to add an argument that comes with a flag. A good exmple of this is the equivalent of Git's commit message.

> tool log --message "First commit"
> tool log -m "First commit"

You can create this behaviour with a Flag<string> parameter.

    [Command]
    public void Log(Flag<string> message)
    {
        Logger.Log(message); 
    }

Notice that the Flag<T> has an implicit cast to T.

The Flag<T> implementation currently also supports enums and int.

> tool loop --count 5
    [Command]
    public void Loop(Flag<int> count)
    { ... }

Assignment parameters

You can provide key value pairs (assignments) as a parameter as well:

> tool login user=john password=secret
    [Command]
    public void Login(Assignment user, Assignment password)
    { ... }

Integer parameters

Integers are also regognised as parameter types. If the user provides anything else than an integer, in this case, the routing will not be match this method

> tool count 5
    [Command]
    public void Count(int max)
    {
        for (int i = 0; i < count; i++) Console.WriteLine(i);
    }

More on parameters

Optional paramters

To make a parameter optional, you can add an [Optional] attribute to it. The value will be set to null or default if it is not provided.

> tool greet John
    [Command]
    public void Greet([Optional]string name)
    {
        if (name is null)
        {
            Console.WriteLine("Hello");
        }
        else 
        {
            Console.Writeline("Hello " + name);
        }
    }

This attribute is not necessary on any type of flag or assignment parameter since they are optional by design.

Parameter aliases

You can create an parameter alias with the [Alt] attribute:

> tool debug --no-color
    [Command]
    public void Debug([Alt("no-color")] Flag nocolor)
    {
    
    }

More on commands

Command Overloading

You can overload your commands. So if you provide two commands with the same name, but different parameter types, the proper command route will be found:

> tool count
> tool count 3
> tool count Count

Each of the above inputs, will route to a different method below:

    [Command]
    public void Count()
    { ... }

    [Command]
    public void Count(int number)
    { ...  }

    [Command]
    public void Count(string name)
    { ... }

Command name aliases

As an opposite of overloading, Console.Routing allows a single command to have multiple aliases.

> tool greet Anna
> tool greeting Anna

Just like the default command names, matching is case insensitive. Bare in mind, that if you add an alias, you should also provide the original name in the list, if you want to make that work as well.

    [Command("greet", "greeting")]
    public void Greet(string name)
    {
        Console.Writeline("Hello " + name);
    }

Aliases also allow you to use commands that C# syntax do not allow. If you must you can parse a flag as a command, but keep in mind that it will just be treated as a literal, so if you need the abbreviation too, you have to add it yourself.

    [Command("info", "give-info", "--info", "-i")]
    public void Info(string name)
    {
        Console.Writeline("Hello " + name);
    }

Hidden commands

If you want a command to be usable, but not showing up in the help, you can use the [Hidden] attribute:

    [Command, Hidden]
    public void Secret()
    { ... }

The Default command

You should always provide a command that respons when the user has given no input at all. This command can also be used for root flags: if no command or sub command has been given.

> tool --help
> tool --info
    [Command, Default]
    public void Info(Flag help, Flag version)
    {
        if (help.Set) ShowHelp();
        if (version.Set) ShowVersion();
    }

Nested Commands

You can create nested commands, or command groups, by marking a Module class as a command in itself:

> tool database update
> tool database drop

This example (borrowed from Entity Frameowrk command line tool), can be constructed like this:

[Module, Command]
public class Database
{
    [Command]
    public void Update() { }
    
    [Command]
    public void Drop() { }
}

You can create deeper nested commands by creating sub classes.

[Module, Command]
public class Main
{
    [Command]
    public class Sub 
    {
        [Command]
        public void SubSub()
        {
        
        }
    }
}

Help text

A command can have an additional [Help] attribute for descriptions in automatic documentation. A mature command line tool, should have a help line for each command.

    [Command, Help("This greeting greets any provided name")]
    public void Greet(string name)
    {
        Console.WriteLine($"Hello {name}")
    }

You can invoke the help documentation by calling PrintHelp:

    [Command, Help("Prints this help text")]
    public void Help(Flag version)
    {
        Routing.PrintHelp();
    }

    [Command, Help("Says hello to name")]
    public void Greet(string name)
    { ... }

The produced help text looks like this:

My tool:
    Help    --version | Prints this help text
    Greet   <name> | Says hello to name

The router gives you access to all route data, if you want to write your own implementation.

Documentation

By default ConsoleRouting provides a help command, that lists all the available visible commands. But it also allows for more indepth documentation by writing

> tool help <command> 

This can optionally be followed bu subcommands. The help can print out four segments:

  1. The route, enabled by default
  2. The description, provided by the [Help( ... )] attribute.
  3. The parameter list. This one can be enriched by providing C# XML inline documentation in your code.
  4. The extendeddocumentation, also provided by the ///<summary> field in the C# XML documentation A fully documentation enriched command will look something like this:
        /// <summary>Says hello to the person in question</summary>
        /// <param name="name"> The name of the person that will be greeted. You can use any name in the known universe </param>
        /// <param name="uppercase">Transforms the name to all caps</param>
        /// <param name="repeats">How many times the greeting should be repeated</param>
        [Command, Help("Says hello to the given name")]
        public void Greet([Optional]string name, bool uppercase, Flag<int> repeats)
        {
            if (uppercase) name = name.ToUpper();
            for(int i = 1; i < (repeats.HasValue ? repeats.Value : 1); i++)
            {
                Console.WriteLine($"Hello {name}!");
            }
        }

This will produce:

> tool help greet
Command:
  Greet (<name>) --uppercase --repeats <value>

Description:
Says hello to the given name

Parameters:
  (<name>)             The name that will be greeted. You can use any name in the known universe
  --uppercase          Transforms the name to all caps
  --repeats <value>    How many times the greeting should be repeated

Documentation:
Says hello to the name in question

In order to enable xml documentation to be published with your tool - necessary for the help enrichment to work, you have to enable it either your build settings (Visual Studio / Project / properties / Build / Output / Xml Documentation file) or directly in the .csproj file of the app or dll where your commands reside:

  <PropertyGroup>
	  <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

Global Settings

You can mark any static class as a global settings class. This allows you to use the same parameters on all commands in your tool. Only properties (not fields) will be set when a matching name is found. And currently only bool is supported (a flag on the command line) But it's the plan to add string and int (valued flags) later.

For this, use the [Global] flag.

[Global]
public static class Settings
{
    public static bool Debug { get; set; }
    public static bool Verbose { get; set; }
}

You can have multple static classes marked as global.

Working with Multiple Projects or Commands not in your Startup Project.

By using the default Routing.Handle(args) all modules and commands in your startup project will be discovered as a route candidate. If you have commands in a different library (dll) or in multiple libraries, you can use the RouteBuilder class instead.

The router builder allows you to add assemblies to the discovery list, either by providing the assembly or a type in the assembly. The example below has some duplicate functionality but it shows you the options:

    var router = new RouterBuilder()
        .Add(Assembly.GetExecutingAssembly())
        .AddAssemblyOf<Program>()
        .Add(AppDomain.CurrentDomain.GetAssemblies())
        .Buid();
    
    router.Handle(args);

Adding services through dependency injection

You can add services available for dependency injection through the RouterBuilder. To add a service, use the .AddService() method.

    var router = new RouterBuilder()
        .AddAssemblyOf<Program>()
        .AddService<SomeService>()
        .Buid();

Capturing

If you want a certain parameter to capture the route, you can add a Capture. If the capture parameter is found anywhere in the argument list, the execution will go to that command, without further resolving other routes.

To have a capture, add a Capture attribute to a command method like this:

    [Command, Capture("--help", "-h")]
    public void Help(Args args)
    {
        Console.WriteLine($"Help for these arguments: {args}");
    }
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.

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
3.3.0-beta-6 143 8/16/2023
3.3.0-beta-5 108 4/23/2023
3.3.0-beta-4 122 4/23/2023
3.3.0-beta-2 132 6/12/2022
3.3.0-beta-1 131 6/5/2022
3.2.2 689 4/7/2022
3.2.0 416 11/1/2021
3.1.2 310 10/14/2021
3.1.1 288 10/14/2021
3.1.0 328 10/13/2021
3.1.0-beta7 219 9/3/2021
3.1.0-beta6 212 9/3/2021
3.1.0-beta5 241 3/17/2021
3.1.0-beta4 230 3/15/2021
3.0.0 407 9/8/2020
3.0.0-beta9 268 7/16/2020
3.0.0-beta8 298 7/9/2020
3.0.0-beta7 328 7/9/2020
3.0.0-beta6 340 7/9/2020
3.0.0-beta5 292 7/7/2020
3.0.0-beta4 388 7/5/2020
3.0.0-beta3 337 7/3/2020
3.0.0-beta2 262 7/2/2020

Added C# inline XML documentation for commands and parameters