FluentArgs
FluentArgs is a library to parse command line arguments. It focuses on a very simple API and strong typing. It is able to generate a help view and proper error messages.
Install / Use
/learn @kutoga/FluentArgsREADME
FluentArgs: Fluent Argument Parsing for .NET
<strong> Version: 0.9.7 </strong>FluentArgs is an easy-to-use library that provides command line argument parsing. For all parameters it is possible to provide meta information (description, examples etc.) which might be used to auto-generate a simple help for the final application.
Why FluentArgs?
The API is optimized to be as readable and type-safe as possible. Therefore, anyone can learn how to use this library in just a few minutes.

How to install
Package Manager:
PM> Install-Package FluentArgs
.NET CLI:
> dotnet add package FluentArgs
Paket CLI:
> paket add FluentArgs
💡 Example: Parse simple arguments and flags
Given you want a program which supports png to jpeg conversion and you want to support calls like these:
myapp -i image.png -o image.jpeg -q 100myapp --input image.png --quality 50 --output image.jpegmyapp --input=image.png -q=50 --output image.jpeg- etc.
There's the code:
namespace Example
{
using System;
using System.Threading.Tasks;
using FluentArgs;
public static class Program
{
public static void Main(string[] args)
{
FluentArgsBuilder.New()
.Parameter("-i", "--input").IsRequired()
.Parameter("-o", "--output").IsRequired()
.Parameter<ushort>("-q", "--quality")
.WithValidation(n => n >= 0 && n <= 100)
.IsOptionalWithDefault(50)
.Call(quality => outputFile => inputFile =>
{
/* ... */
Console.WriteLine($"Convert {inputFile} to {outputFile} with quality {quality}...");
/* ... */
})
.Parse(args);
}
}
}
You might wonder why the order of parameters for the Call-method are inverted. This is due to a limitation
of the C#-programming language: If the order should be reversed, the number of parameters has to be limited
to a fixed number. At least it is not obvious how something like variadic templates can be implemented.
You want to have a detailed help? Just annotate all parameters and call myapp -h or myapp --help.
The help flag is added by the DefaultConfigs...-call. As you can see later, it is possible to disable the
help flag, to use a different help flag name or to customize the help output. It is also possible use async
code.
In general it is recommended to add DefaultConfigs() to the parser: It adds the help flags (which still
might be overwritten) and some additional validations (see Example: Advanced configuration).
namespace Example
{
using System;
using System.Threading.Tasks;
using FluentArgs;
public static class Program
{
public static Task Main(string[] args)
{
return FluentArgsBuilder.New()
.DefaultConfigsWithAppDescription("An app to convert png files to jpg files.")
.Parameter("-i", "--input")
.WithDescription("Input png file")
.WithExamples("input.png")
.IsRequired()
.Parameter("-o", "--output")
.WithDescription("Output jpg file")
.WithExamples("output.jpg")
.IsRequired()
.Parameter<ushort>("-q", "--quality")
.WithDescription("Quality of the conversion")
.WithValidation(n => n >= 0 && n <= 100)
.IsOptionalWithDefault(50)
.Call(quality => outputFile => inputFile =>
{
/* ... */
Console.WriteLine($"Convert {inputFile} to {outputFile} with quality {quality}...");
/* ... */
return Task.CompletedTask;
})
.ParseAsync(args);
}
}
}
💡 Example: Parse positional and remaining arguments
Positional arguments without an explicit name might be used if the context defines their meaning. E.g.
find --type f ./my_directory shall be parsed. An equivalent call is find ./my_directory --type f. The
source directory is a positional argument.
Such arguments can be defined after all simple arguments and flags are defined:
<details> <summary>📃 Click here to see the code</summary>namespace Example
{
using System;
using System.Threading.Tasks;
using FluentArgs;
public static class Program
{
public static Task Main(string[] args)
{
return FluentArgsBuilder.New()
.DefaultConfigsWithAppDescription("List files and / or subdirectories")
.Parameter<char>("-t", "--type")
.WithDescription("List entry type (e.g. f=file, d=directory)")
.IsOptionalWithDefault('d')
.PositionalArgument()
.WithDescription("The source directory")
.IsRequired()
.Call(sourceDirectory => type =>
{
/* ... */
Console.WriteLine($"Find all {type} filesystem entries in the directory {sourceDirectory}");
/* ... */
return Task.CompletedTask;
})
.ParseAsync(args);
}
}
}
</details>
It is no problem to define multiple positional arguments:
<details> <summary>📃 Click here to see the code</summary>namespace Example
{
using System;
using System.Threading.Tasks;
using FluentArgs;
public static class Program
{
public static Task Main(string[] args)
{
return FluentArgsBuilder.New()
.PositionalArgument().IsRequired()
.PositionalArgument<int>().IsRequired()
.PositionalArgument<bool>().IsOptionalWithDefault(false)
.Call(p3 => p2 => p1 =>
{
/* ... */
Console.WriteLine($"First parameter: {p1}");
Console.WriteLine($"Second parameter: {p2}");
Console.WriteLine($"Third parameter: {p3}");
/* ... */
return Task.CompletedTask;
})
.ParseAsync(args);
}
}
}
</details>
It is also possible to parse all remaining arguments. E.g., if calls like rm -f file1 file2 file should
be supported (with an arbitrary number of files), this can be achieved by the following code:
namespace Example
{
using System;
using System.Threading.Tasks;
using FluentArgs;
public static class Program
{
public static Task Main(string[] args)
{
return FluentArgsBuilder.New()
.Flag("-f")
.WithDescription("Force to delete a file")
.LoadRemainingArguments()
.WithDescription("All files which should be deleted")
.Call(files => f =>
{
/* ... */
Console.WriteLine($"f-Flag: {f}");
Console.WriteLine($"Files: {string.Join(", ", files)}");
/* ... */
return Task.CompletedTask;
})
.ParseAsync(args);
}
}
}
</details>
💡 Example: Parse conditional arguments / commands
Conditional arguments allow to control the argument parsing flow. E.g., the requirements for our CLI is the following:
- If the flag
-v(or--version) is given, the program version should be print (independent of all other parameters) - If the flag
-u(or--update) is given, an update should be downloaded and installed- An optional parameter
-s(or--source) defines the update source
- An optional parameter
- Otherwise the program takes it first two positional arguments and prints their sum:
myapp 1 2should print1+2=3
The following code fulfills this specifications:
<details> <summary>📃 Click here to see the code</summary>namespace Example
{
using System;
using System.Threading.Tasks;
using FluentArgs;
public static class Program
{
public static void Main(string[] args)
{
FluentArgsBuilder.New()
.WithApplicationDescription("A simple calculator: add two numbers")
.Given.Flag("-v", "--version").Then(() =>
{
/* ... */
Console.WriteLine("Program version: 2.0");
/* ... */
})
.Given.Flag("-u", "--update").Then(b => b
.Parameter<Uri>("-s", "--source")
.WithDescription("Update source url")
.IsOptionalWithDefault(new Uri("http://my-update-server.com/update.zip"))
.Call(uri =>
{
/* ... */
Console.WriteLine($"Install update from {uri}...");
/* ... */
}))
.PositionalArgument<int>()
.WithDescription("The first number")
.IsRequired()
.PositionalArgument<int>()
.WithDescription("the second number")
.IsRequired()
.Call(n2 => n1 =>
{
/* ... */
Console.WriteLine($"{n1}+{n2}={n1 + n2}");
