About EntryPoint
Entrypoint is an argument parser designed to be composable, practical to use, and maintainable over the life time of a project.
It's based around the concept of Declarative POCOs (CliArguments, and CliCommands classes), which you simply pass to EntryPoint for parsing and construction.
Parses arguments in the form UtilityName [command] [-o | --options] [operands]
Standards
- .Net Standard 1.6+ (All future .Net releases are built on this)
- .Net Framework 4.5+
Follows the IEEE Standard for command line utilities
closely, but does include common adblibs, such as fully named --option style options.
Installation
EntryPoint is available on NuGet:
PM> Install-Package EntryPoint
Introduction
There are just a few classes you'll interact with:
Cli- The main API, which handles all processingBaseCliArguments- An abstract class which you implement to define Options & Operands, known as CliArguments classesBaseCliCommands- An abstract class which you implement to define Commands, known as CliCommands classesAttributes- There are a handful of attributes you can use to define your CliCommands and CliArguments implementations, which are documented in depth below
Arguments
Basic Usage
For a simple application you may not need Commands; CliArguments classes are used
to parse command line arguments without consideration of Commands.
Let's say we want a utility used like: UtilityName [-s] [--name Bob] [6.1]
This has one Option (-s), one OptionParameter (--name Bob) and a positional Operand (6.1)
class SimpleProgram {
void main(string[] args) {
// One line parsing of any `BaseCliArguments` implementation
var arguments = Cli.Parse<SimpleCliArguments>(args);
// Object oriented access to your arguments
Console.WriteLine($"The name is {arguments.Name}");
Console.WriteLine($"Switch flag: {arguments.Switch}");
Console.WriteLine($"Positional Operand 1: {arguments.FirstOperand}");
}
}
class SimpleCliArguments : BaseCliArguments {
public SimpleCliArguments() : base("SimpleApp") { }
// Defining a CliArguments class is as easy as adding
// attributes to your POCO properties
[Option(LongName: "switch",
ShortName: 's')]
public bool Switch { get; set; }
// Both Option and OptionParameter attributes support a
// combination of -o and --option style invocations
[OptionParameter(LongName: "name",
ShortName: 'n')]
public string Name { get; set; }
// Operands can be mapped positionally
// But BaseCliArguments also has a .Operands string[] where
// un-mapped operands are stored
[Operand(position: 1)]
public decimal FirstOperand { get; set; }
}
CliArguments Attributes
We use Attributes to define CLI functionality
[Option(LongName = string, ShortName = char)]
- Apply to: Class Properties
- Output Types: Bool
- Detail: Defines an On/Off option for use on the CLI
- Argument, LongName: the case in-sensitive name to be used like
--name - Argument, ShortName: the case sensitive character to be used like
-n - At least one name needs to be provided
[OptionParameter(LongName = string, ShortName = char)]
- Apply to: Class Properties
- Output Types: Primitive Types, Enums
- Detail: Defines a parameter which can be invoked to provide a value
- Argument, LongName: the case in-sensitive name to be used like
--name - Argument, ShortName: the case sensitive character to be used like
-n - At least one name needs to be provided
[Operand(position = int)]
- Apply to: Class Properties
- Output Types: Primitive Types, Enums
- Detail: Maps a positional operand from the end of a CLI command
- Argument, Position: the 1 based position of the Operand
[Required]
- Apply to: Option, OptionParameter or Operand properties
- Detail: Makes an Option or Operand mandatory for the user to provide
[Help(detail = string)]
- Apply to: Class Properties with any Option or Operand Attribute applied, or an CliArguments Class
- Detail: Provides custom documentation on an Option, Operand or CliArguments Class, which will be consumed by the help generator
Example Application
The following is an example implementation for use in a simple message sending application
This is used like UtilityName [ -v | --verbose ] [ -s | --subject "your subject" ] [ -i | --importance [ normal | high ] ] [message]
// Usage is as simple as
class MessagingProgram {
void main(string[] args) {
var arguments = Cli.Parse<MessagingCliArguments>(args);
// Use the arguments object...
}
}
class MessagingCliArguments : BaseCliArguments {
public MessagingCliArguments() : base("Message Sender") { }
// Verbose will be a familiar option to most CLI users
[Option(LongName: "verbose",
ShortName: 'v')]
[Help("When this is set, verbose logging will be activated")]
public bool Verbose { get; set; }
// A subject *must* be provided by the user
[Required]
[OptionParameter(LongName: "subject",
ShortName: 's')]
[Help("Mandatory Subject to provide")]
public string Subject { get; set; }
// An enum importance level for the message.
// If not provided this is defaulted to `Normal`
// User can provide the value as a number or string (ie. '2' or 'high')
[OptionParameter(LongName: "importance",
ShortName: 'i')]
[Help("Sets the importance level of a sent message")]
public MessageImportanceEnum Importance { get; set; } = MessageImportanceEnum.Normal;
// A list of strings
// Lists support all the same types as any other option parameter
// The Cli expects list values in the form `item1,item2,item3` etc
[OptionParameter(LongName: "recipients")]
[Help("A list of email addresses to send to")]
public List<string> Recipients { get; set; }
// A message *must* be provided as the first operand
[Required]
[Operand(1)]
[Help("Mandatory message to provide")]
public string Message { get; set; }
}
enum MessageImportanceEnum {
Normal = 1,
High = 2
}
Value Defaults
If the user does not provide an non-required option-parameter or operand, it can be useful to configure the application with a default.
This is easily done using C# property initialisers, and will otherwise use the type's default value
// The following Importance Enum will always be set to 'Normal'
// if the user does not provide a value
[OptionParameter(LongName: "importance",
ShortName: 'i')]
[Help("Sets the importance level of a sent message")]
public MessageImportanceEnum Importance { get; set; } = MessageImportanceEnum.Normal;
Commands
Introduction
Although it's perfectly fine to only use a CliArguments class for a simple application, if you have multiple Commands, each with a different set of Arguments; you may want to create multiple application entry points, each with its own CliArguments class.
This is the purpose of BaseCliCommands.
Basic Usage
class SimpleCommandsProgram {
public void Main(string[] args) {
// One line execution of the Commands class
// It will select and route to one of your Command methods
var commands = Cli.Execute<SimpleCliCommands>(args);
}
}
class SimpleCliCommands : BaseCliCommands {
// A command is a Method which takes a `string[]`.
// You also need to apply a [Command(name)] attribute,
// with the name of the command on the CLI
[Command("command1")]
public void Command1(string[] args) {
// var arguments = Cli.Parse<Command1CliArguments>(args);
// ...Application logic
}
// You can also define a Default command.
// This helps if you want a fallback when the user doesn't name a command
[DefaultCommand]
[Command("command2")]
public void Command2(string[] args) {
// var arguments = Cli.Parse<Command2CliArguments>(args);
// ...Application logic
}
}
CliCommands Attributes
There are several attributes which can be applied to a CliCommands class
[Command(Name = string)]
- Apply to: Methods with the signature:
void MethodName(string[]) - Argument, Name: This is the Command Name to be used on the CLI like:
Utility [Command Name] [options] - Detail: Defines a method as a Command to be routed to
[DefaultCommand]
- Apply to: Command m\ethods
- Detail: Defines a Command as the default when no Command is specified, otherwise EntryPoint invokes
--help
[Help(detail = string)]
- Apply to: CliCommands classes and Command methods
- Detail: Provides custom documentation on a Command ## Help Generator
Introduction
EntryPoint provides an automatic Help generator, which always owns the -h and --help
Options in both CliCommands and CliArguments instances.
When --help is invoked by the user, .HelpInvoked is set on CliCommands/CliArguments,
and the virtual method OnHelpInvoked(string helpText) is invoked.
By default OnHelpInvoked will print the help text to screen, and call Environment.Exit(0).
By overriding OnHelpInvoked on a CliCommands/CliArguments implementation,
you change the implementation to something more appropriate to your program flow.
EntryPoint does not try to control your usage of this, but be aware that invoking --help will
disable Required attributes; if you neither exit or check .HelpInvoked, then your
program may continue running with invalid state.
Basic Usage
The Help Generator consumes the following information for each class type.
CliCommands
[Help("This will be displayed as an initial blurb for the utility")]
class HelpCliCommands : BaseCliCommands {
// [DefaultCommand]
// [Command(...)]
[Help("Displayed as instructions for a command")]
public void CommandMethod(string[] args) {
// ...
}
}
CliArguments
[Help("This will be displayed as an initial blurb for the command/utility")]
class HelpCliArguments : BaseCliArguments {
public HelpCliArguments()
: base(utilityName: "Displayed as the command/utility name") { }
// [Option(...)]
// [OptionParameter(...)]
// [Operand(...)]
[Help("Displayed as additional instructions for an Option/Operand")]
public bool Value { get; set; }
}
A simple implementation would therefore look like this:
[Help("This will be displayed as an initial blurb for the utility")]
class ExampleHelpCliCommands : BaseCliCommands {
// [DefaultCommand]
[Command("command1")]
[Help("Some command that can be used")]
public void Command1(string[] args) {
// ...etc
}
// This will run if --help is invoked, print help and exit the program
public override void OnHelpInvoked(string helpText) {
Console.WriteLine(helpText);
Environment.Exit(0);
}
}
class CommandsHelpProgram {
public void main(string[] args) {
var commands = Cli.Execute<ExampleHelpCliCommands>(args);
// Execution would not reach this point if --help is invoked,
// since OnHelpInvoked would run and exit the program
// However, if you don't want to implement OnHelpInvoked,
// you could also do this:
if (commands.HelpInvoked) {
// Return here, or run something else
}
// Normal Post-Command Application code...
}
}
...and the same thinking applies to CliArguments
[Help("This will be displayed as an initial blurb for the command/utility")]
class ExampleHelpCliArguments : BaseCliArguments {
public ExampleHelpCliArguments()
: base(utilityName: "Displayed as the command/utility name") { }
[OptionParameter(LongName: "value1")]
[Help("Some value to set")]
public bool Value1 { get; set; }
public override void OnHelpInvoked(string helpText) {
Console.WriteLine(helpText);
Environment.Exit(0);
}
}
class ArgumentsHelpProgram {
public void main(string[] args) {
var arguments = Cli.Parse<ExampleHelpCliArguments>(args);
// Execution would not reach this point if --help is invoked,
// since OnHelpInvoked would run and exit the program
// However, if you don't want to implement OnHelpInvoked,
// you could also do this:
if (arguments.HelpInvoked) {
// Return here, or run something else
}
// Normal Application code...
}
}
Tips & Behaviour
Cli.ParseandCli.Executehave several overloads available. They can create the class and get the command line arguments themselves, but give you manual control, too.- Short named options
-oare case sensitive:-a != -A - Long named options
--optionare case insensitive:--opt == --Opt - Options can be combined by the user:
-a -b -c->-abc - Combined options can end with an option parameter:
-abco value - Option-parameters have several forms:
-o value-o=value--option value--option=value - Quotes and Escape characters are both supported:
--option "my value"--option \-my-value - Warning: be careful with Quotes as .Net respects and then removes them during
string[] argscreation. They can be escaped to include in values.