Verb Commands

Giacomo Stelluti Scala edited this page Aug 7, 2015 · 28 revisions

IMPORTANT NOTE: This wiki refers to latest stables, if a beta version is available in github master branch please refer to Latest Version.

From Version 1.9.4.91 you get the opportunity to define a syntax similar to git or other popular command line tools.

$ git commit -a
$ git push -u origin commit
$ git tag -a tagname -m 'Tag Description'

commit, push, and tag are all verb commands. The options that follows are options ordinary, defined as specified in Mapping Properties To Options.

Define Model For Verbs And Options

To begin we will define three ordinary options class:

class CommitSubOptions
{
  [Option('a', "all", HelpText="Tell the command to automatically stage files.")]
  public bool All { get; set; }
  // Remainder omitted
}

class PushSubOptions
{
  // Remainder omitted
}

class TagSubOptions
{
  // Remainder omitted 
}

If the remainder were not omitted, XXXSubOptions::GetUsage() would not have been defined. Now we can define the master options class that will host all these sub options.

class Options
{
  public Options()
  {
    // Since we create this instance the parser will not overwrite it
    CommitVerb = new CommitSubOptions {Patch = true};
  }

  [VerbOption("commit", HelpText = "Record changes to the repository.")]
  public CommitSubOptions CommitVerb { get; set; }

  [VerbOption("push", HelpText = "Update remote refs along with associated objects.")]
  public PushSubOptions AddVerb { get; set; }

  [VerbOption("tag", HelpText = "Update remote refs along with associated objects.")]
  public TagSubOptions TagVerb { get; set; }
}

Subsequent call to ParseArguments() occurs as follows:

string invokedVerb;
object invokedVerbInstance;

var options = new Options();
if (!CommandLine.Parser.Default.ParseArguments(args, options,
  (verb, subOptions) =>
    {
      // if parsing succeeds the verb name and correct instance
      // will be passed to onVerbCommand delegate (string,object)
      invokedVerb = verb;
      invokedVerbInstance = subOptions;
    }))
{
  Environment.Exit(CommandLine.Parser.DefaultExitCodeFail);
}

if (invokedVerb == "commit") {
  var commitSubOptions = (CommitSubOptions)invokedVerbInstance;
}
// Remainder omitted

By design verbs can be specified only as first argument, they can't live side by side with ordinary options and they are implicitly mutually exclusive. If you have common options you can define it in a base class:

abstract class CommonSubOptions
{
  [Option('q', "quiet",
    HelpText = "Suppress summary message.")]
  public bool Quiet { get; set; }
}

class CommitSubOptions : CommonSubOptions
{
  // Remainder omitted
}

Instances Management

Since its inception this library was built around this founding philosophy: respect as much as possible developers programmer style. This the reason why CommandLineOptionsBase was removed, we did not want our base types propagating through your class hierarchies.

For this reason if you want the parser create a sub option class for you, just define the class with a parameterless constructor. But if you want

  • define different constructors
  • use object initializers
  • take advantage of IoC/DI frameworks
  • deserialize it from a persistence layer

or simply create the instance by your own, just do it. The parser will not try overwrite an existing instance.

Relations To Help Sub-System

HelpText was updated to render verbs help index (to be honest only HelpText::AutoBuilder and related code was slightly modified, because it already allows you to print options without dashes). You do not need (and should not) define GetUsage for sub options.

The parser will look in the master option class for method that accepts and returns a string marked with HelpVerbOptionAttribute. Let's take a look at an example:

class Options
{
  // Remainder omitted
  [HelpVerbOption]
  public string GetUsage(string verb)
  {
    return HelpText.AutoBuild(this, verb);
  }
}

From developer perspective you only need to know that if verb == null, you have to print the help index (an help screen that summarizes all verb commands). But if verb == "a-verb-command", you should print the help screen of sub options that belong to requested verb.

The parser will pass null to master class GetUsage(string) also if the user requested the help index with:

$ git help

or the verb command if the user requested explicitly instructions on how to use a particular verb:

$ git help commit

Anyway HelpText::AutoBuild(object,string) will take care of everything.