A command line parser - console is the new UI.
To start with, if are going down the road of building a ground up CLI tool you might be able to steal some ideas from this but this is a very opinionated piece of tooling. What I was aiming for was a way to wrap libraries up and execute from the commandline. The usage example uses a library that generates hexagon coordinates and outputs them in either WKT or GeoJson.
Three parts are needed to use this CLI tooling (in addiition to the tooling itself):
- a library that contains the functionality that you will be exposing
- a project that wraps the functionality aoe in a usable way and defines the command arguments
- a console application that inherits from
ConsoleDecoator
The external project implementation of this is the working proof-of-concept.
Really? it's a nuget package - they got this covered.
:> sample usage c:/temp/Animals.txt --type file -m "There are dog about" --shouldBeCareful
// ^ ^ ^ ^ ^ ^ ^ ^
// | verb | | | | quoted option value boolean option key
// driver positional argument | | short option key
// | option value
// long option
There are three parts to the command line implementation:
- The driver is the executable that runs the process, this tool does not affect this.
- The verb is the argument and command selector.
- The remaining items are the arguments, either named or positional.
- Positional arguments are order dependent
- Option arguments are key-value pairs
- Any argument containing a space must be enclosed in quotes
- Boolean option arguments do not require a value and are true if present and false if not present
- Arguments can be the simple types boolean, integer, floating point or string
- Arguments can be arrays of the above simple types, arrays contain values separated by commas
- Option arguments can be either short or long form
- Long form are preceded by
--
- Short form are preceded by
-
- Long form are preceded by
The plugin functionality requires (at this point) an Dependency Injector (DI) (a.k.a. Inversion of Control (IoC)). For the purposes of this example Simple Injector is used, however this should work with any injector or even custom tooling.
We are using package or module registration of the DI container to create plugins.
This can also be achieved using plugin registration if the DI has that.
class Program
{
static void Main(string[] args)
{
// add a DI container
var di = ConfigureDI(args);
var verb = args != null && args.Length > 0 ? args[0] : String.Empty;
var commands = di.GetAllInstances<IPlugin>().ToList();
var command = commands
.Where(p => string.Equals(p.Name, verb, StringComparison.CurrentCultureIgnoreCase))
//.DefaultIfEmpty(new HelpWithVerbs(commands))
.Single();
var result = command.Execute();
Console.WriteLine(result);
}
private static Container ConfigureDI(string [] args)
{
var container = new Container();
var pluginAssemblies = PluginAssemblies("");
container.RegisterPackages(pluginAssemblies);
container.Collection.Register<IPlugin>(pluginAssemblies);
container.RegisterInstance<IArgs>(new Args(args));
#if DEBUG
container.Verify();
#endif
return container;
}
private static List<Assembly> PluginAssemblies(string pluginDirectory)
{
var binDirectory = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, pluginDirectory);
var assemblyNames = Directory.GetFiles(binDirectory)
.Where(f => Path.GetExtension(f) == ".dll")
.Select(f => Path.GetFullPath(f));
var assemblies =
from assembly in assemblyNames
select Assembly.LoadFile(assembly);
var pluginAssemblies = assemblies.ToList();
return pluginAssemblies;
}
}
There are four principal parts to a plugin module:
- The plugin class which inherits and implements the abstract class
Plugin<TArgs>
, by convention this class is called verbPlugin. - The args class which inherits the
CommandArgs
class. This class is the commandline arguments as a plain data object. - The command class which inherits for
ICommand<Targs>
, the actual functionality of the plugin is contained within theExecute()
method. - The package class which contains the DI container module or package configuration.
- document the various deployment and development strategies for plugins