JM.CLImber 1.0.1

dotnet add package JM.CLImber --version 1.0.1
NuGet\Install-Package JM.CLImber -Version 1.0.1
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="JM.CLImber" Version="1.0.1" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add JM.CLImber --version 1.0.1
#r "nuget: JM.CLImber, 1.0.1"
#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 JM.CLImber as a Cake Addin
#addin nuget:?package=JM.CLImber&version=1.0.1

// Install JM.CLImber as a Cake Tool
#tool nuget:?package=JM.CLImber&version=1.0.1

CLImber - A Command Line Interface Library Build and Test

Introduction

CLImber is a .Net library aimed at offloading the work of setting up, documenting, and parsing command line arguments. CLImber should allow your code to be smaller and more concisely focused on the actual work at hand rather than the plumbing required to support a robust command line interface.

CLImber was motivated by writing argument parsing logic over and over again for small utility projects. The need was highlighted as the functional code in my projects was dwarfed by the code required to parse and handle more and more options.

Technologies

My goal with CLImber is to keep it as small and as portable as possible.

  • .NET Standard 2.0 & C# v7.3

That's it. There are no other libraries, dependencies, or technologies. It should be noted that CLImber does use reflection to do most of its work.

Setup

Search for JM.CLImber on NuGet

Use

CLImber defines 3 types of command line artifacts. These are inspired by the git command line, so we will use that as an example:

git checkout -b new_branch

checkout is a command. Commands are considered the primary element of work. In this case we know that checkout is going to switch our working directory to another branch.

-b is an option. Options are used to modify the behavior of commands. -b is telling the checkout command to create a new branch.

git config --global user.name

--global is a full option. It is dilineated by '--' at the beginning. Full options can also take arguments. Options other than boolean are expected to take a value. The value can be designated following an equal sign: --intOption=7 or values can be supplied as the next argument in the command line: --intOption 7.

Options can also be entered in abbreviated form. Abbreviated options are preceded by a single '-' character and always use a single character. -b is an example of an abbreviated option. Abbreviated options can be convenient becaues they can be combined (or 'aggregated' to use the git nomenclature): -bxytj would represent 5 different abbreviated options. Abbreviated options can have a value provided the same way that full options can. However, when aggregating options only one option per group can take a value. Other options that require a value must be seperated: incorrect: -abcd 7 foo (assuming both c and d require values). correct: -abc 7 -d foo

new_branch is an argument. An argument provides additional information to the command so it can complete a task. This example is providing checkout with the name of the branch to create and then checkout.

Implementation

CLImber maps each of the command line arguments to specific code elements. Commands map to classes; Options map to properties; and arguments map to method parameters;

The following is an example of a class that would handle the checkout command detailed previously:

using CLImber;

namespace CLImber.Example
{
    [CommandClass("checkout")]
    public class CheckoutCommand
    {
        [CommandOption("new-branch", Abbreviation='b']
        public bool NewBranch { get; set; }
        
        [CommandHandler]
        public void Checkout(string branchName)
        {
            ///Do the actual work here.
        }
    }
}

Register the checkout command with CLImber

At runtime, CLImber uses reflection to scan the codebase and find all classes decorated with the CommandClass attribute. In our code example we have decorated our CheckoutCommand class with the CommandClass attribute and provided a "checkout" argument. This argument determines the name of the command that CLImber will associate with this class.

    ...
    [CommandClass("checkout")]
    public class CheckoutCommand
    {
    ...

Create the new-branch option

The git checkout command accepts an option to create a new branch instead of switching to an existing branch. To implement this in CLImber we create a property in our CheckoutCommand class:

    ...
        [CommandOption("new-branch", Abbreviation='b']
        public bool NewBranch { get; set; }
    ...

By decorating our property with the CommandOption attribute CLImber will recognize that this property is an option for the checkout command. The first argument is the full option name which would be invoked by using --new-branch on the command line. The Abbreviation parameter designates the character used to invoke this option using abbreviated syntax. On the command line this would look like: -b.

Create the command handler method

We have already flagged the CheckoutCommand class as representing the checkout command. But we haven't told CLImber that we are expecting a single string argument to be provided when the checkout command is invoked. To do this we add a CommandHandler method to our class like this:

    ...
        [CommandHandler]
        public void Checkout(string branchName)
        {
            ///Do the actual work here.
        }
    ...

When the checkout command is invoked CLImber will scan the CheckoutCommand class for CommandHandler methods. Assuming a branch name was provided CLImber will find all CommandHandler methods that accept 1 argument and then try to invoke those methods. In this case because our Checkout method accepts a single string argument CLImber would pass the branch name provided by the user.

Let CLImber Handle your CLI

Now that we've created this class and properly designated the options and handler methods all that is left to do is tell our program that CLImber needs to handle our CLI arguments. In its simplest form:

static void Main(string[] args)
{
    (new CLIHandler()).Handle(args);
}

Summary

This is a simple example of implementing a command using CLImber, but CLImber can do more. Methods can have arguments other than string and CLImber will attempt to convert to the appropriate type. Arrays can be used in Handler methods to handle any number of arguments. CLImber also includes a rudimentary dependency injection system. CLImber can also enumerate commands, options, and arguments so createing relevant on-screen help is simpler too. Documentation for all these features will be available in the wiki, as soon as possible.

With simple attributes and a little bit of reflection CLImber handles the discovery of commands and the methods to call to make sure everything is handled correctly. Your project code remains cleaner and focused on achieving the operational goals.

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
1.0.1 191 12/11/2023
1.0.0 103 12/10/2023
0.6.0 416 9/8/2021
0.5.3 290 3/29/2021
0.5.1 291 3/29/2021
0.5.0 357 3/27/2021
0.4.0 323 3/13/2021
0.3.0 329 3/3/2021
0.2.0 310 2/20/2021

Bug fix: now accepts arguments with duplicate values.