A simple to use, declarative, type-safe command line parser, with beautiful help messages and clear errors, suitable for simple scripts and complex tools.
Therapist allows you to use a carefully constructed tuple to specify how you want your commandline arguments to be parsed. Each value in the tuple must be set to a <Type>Arg of the appropriate type, which specifies how that argument will appear, what values it can take and provides a help string for the user.
A simple 'Hello world' example:
import therapist # The parser is specified as a tuple let spec = ( # Name is a positional argument, by virtue of being surrounded by < and > name: newStringArg(@["<name>"], help="Person to greet"), # --times is an optional argument, by virtue of starting with - and/or -- times: newIntArg(@["-t", "--times"], defaultVal=1, help="How many times to greet"), # --version will cause 0.1.0 to be printed version: newMessageArg(@["--version"], "0.1.0", help="Prints version"), # --help will cause a help message to be printed help: newHelpArg(@["-h", "--help"], help="Show help message"), ) # `args` and `command` would normally be picked up from the commandline spec.parseOrQuit(prolog="Greeter", args="-t 2 World", command="hello") # If a help message or version was requested or a parse error generated it would be printed # and then the parser would call `quit`. Getting past `parseOrQuit` implies we're ok. for i in 1..spec.times.value: echo "Hello " & spec.name.value doAssert spec.name.seen doAssert spec.name.value == "World" doAssert spec.times.seen doAssert spec.times.value == 2
The above parser generates the following help message
Greeter Usage: hello <name> hello --version hello -h|--help Arguments: <name> Person to greet Options: -t, --times=<times> How many times to greet [default: 1] --version Prints version -h, --help Show help message
The constructor for each <Type>Arg type takes the form:
# doctest: skip proc newStringArg*(variants: seq[string], help: string, defaultVal="", choices=newSeq[string](), helpvar="", required=false, optional=false, multi=false, env="", helpLevel=0)
Creating your own ValueArg is as simple as defining a parse method that turns a string into a value of an appropriate type (or raises a ValueError for invalid input). Suppose we want to create a DateArg type that only accepts ISO-formatted dates:
import therapist import times let DEFAULT_DATE = initDateTime(1, mJan, 2000, 0, 0, 0, 0) proc parseDate(value: string): DateTime = parse(value, "YYYY-MM-dd") defineArg[DateTime](DateArg, newDateArg, "date", parseDate, DEFAULT_DATE)
Now we can call newDateArg to ask the user to supply a date
At the other extreme, you can create complex parsers with subcommands (the example below may be familiar to those who have seen docopt.nim). Note that the help message is slightly different; this is in part because parser itself is stricter. For example, --moored is only valid inside the mine subcommand, and as such, will only appear in the help for that command, shown if you run navel_fate mine --help.
import options import strutils import therapist let prolog = "Navel Fate." let create = ( name: newStringArg(@["<name>"], multi=true, help="Name of new ship") ) let move = ( name: newStringArg(@["<name>"], help="Name of ship to move"), x: newIntArg(@["<x>"], help="x grid reference"), y: newIntArg(@["<y>"], help="y grid reference"), speed: newIntArg(@["--speed"], defaultVal=10, help="Speed in knots"), help: newHelpArg() ) let shoot = ( x: newIntArg(@["<x>"], help="Name of new ship"), y: newIntArg(@["<y>"], help="Name of new ship"), help: newHelpArg() ) let state = ( moored: newCountArg(@["--moored"], help="Moored (anchored) mine"), drifting: newCountArg(@["--drifting"], help="Drifting mine"), ) let mine = ( action: newStringArg(@["<action>"], choices = @["set", "remove"], help="Action to perform"), x: newIntArg(@["<x>"], help="Name of new ship"), y: newIntArg(@["<y>"], help="Name of new ship"), state: state, help: newHelpArg() ) let ship = ( create: newCommandArg(@["new"], create, help="Create a new ship"), move: newCommandArg(@["move"], move, help="Move a ship"), shoot: newCommandArg(@["shoot"], shoot, help="Shoot at another ship"), help: newHelpArg() ) let spec = ( ship: newCommandArg(@["ship"], ship, help="Ship commands"), mine: newCommandArg(@["mine"], mine, help="Mine commands"), help: newHelpArg() ) let (success, message) = spec.parseOrMessage(prolog="Navel Fate.", args="--help", command="navel_fate") let expected = """ Navel Fate. Usage: navel_fate ship new <name>... navel_fate ship move <name> <x> <y> navel_fate ship shoot <x> <y> navel_fate mine (set|remove) <x> <y> navel_fate -h|--help Commands: ship Ship commands mine Mine commands Options: -h, --help Show help message""".strip() doAssert success and message.isSome doAssert message.get == expected
Many more examples are available in the source code and in the nimdoc for the various functions.
In rough order of likelihood of being added:
Clone the repository and then run:
> nimble install
The code lives on bitbucket. Pull requests (with tests) and bug reports welcome!
This library is made available under the LGPL. Use it to make any software you like, open source or not, but if you make improvements to therapist itself, please contribute them back.
This is therapist. There are many argument parsers like it, but this one is mine. Which one you prefer is likely a matter of taste. If you want to explore alternatives, you might like to look at:
Initial release