A simple, focused and expressive library for building command line applications. Optimised for native ReasonML/OCaml.
Features
- autocompletion - fast and comprehensive autocompletion of commands, arguments and values
- sync and async commands support
- sub commands - you can easily define a whole tree of commands and compose them
- auto configuration validation - you can validate your whole commands tree configuration with a single function call in your tests
- auto help generation
- autocorrection
API Reference
Note that the only external modules are:
Usage
- To add to your esy project simply use:
esy add rarg
- Define command arguments with Args.
module Args = Rarg.Args;module Type = Rarg.Type; module MyCmd = { let args = [] let (args, getCopy) = Args.One.boolFlag( ~args, ~name="--copy", ~doc="Whether to copy", Type.bool, ); let (args, getColor) = Args.One.default( ~args, ~name="--color", ~doc="Paint color", ~default="green", Type.string, ); // ...};
For the Type argument you can either choose one of the predefined argument types or define custom argument types, for example:
// ... type fruit = | Apple | Banana; let fruit: Type.t(fruit) = { name: "fruit", parse: fun | "apple" => Ok(Apple) | "banana" => Ok(Banana) | x => Error(Some(x ++ " is not a fruit.")), stringify: fun | Apple => "apple" | Banana => "banana", choices: Some(HelpAndSuggestions([Apple, Banana])), }; let (args, getFruits) = Args.Many.req(~args, ~name="--fruits", ~doc="Fruits", fruit); // ...
- Define the command with Cmd:
// ... // Define the function that you want to execute let handle = (~fruits: list(fruit), ~copy: bool, ~color: string) => (); // Define a mapping function that will use the getters returned from `Args` // and pass the provided user arguments. // It allows you to use labeled arguments as opposed to relying on arg positions. let run = m => handle(~fruits=getFruits(m), ~copy=getCopy(m), ~color=getColor(m)); // Define a command record that you can use to run your command, // pass it as a child to other commands or test it let cmd: Cmd.t(unit) = Cmd.make(~name="My Command", ~version="1.0", ~args, ~run, ());} // module MyCmd close
You can also easily define sub commands:
module AnotherCmd = { // ... let cmd: Cmd.t(unit) = Cmd.make( ~name="Another Command", ~version="1.0", ~args, ~run, ~children=[("my-cmd", MyCmd.cmd)], (), );};
In
rarg
every command/subcommand is a complete unit of work, that can exist on its own, has no dependencies of its parents. That's why every command has its own version.
- And finally you can run your command with Run
let main = { switch (Run.autorun(MyCmd.cmd)) { | Ok(_) => exit(0) | Error(_) => exit(1) };};
System arguments (auto-included)
--help
- display command help--version
- display command version--rarg-suggestions-script
- displays a script with instsructions how to install it to enable shell autocompletions--rarg-add-path
- displays a script with instructions how to add the app executable to the user's path (helpful during development)
Examples
You can check the local examples or the repo rarg-examples for more complete examples.
cmdliner
Comparison withThis was the most requested comparison and is added for completeness, but the 2 are very different.
cmdliner
is hosted onopam
|rarg
onnpm
- it's likely that you would be more familiar with
cmdliner
's API if you have anOCaml
background and withrarg
's API if you are coming from other languages (includingJS
) cmdliner
is very mature and has a large ecosystem behind itrarg
has autocompletions, smaller API footprint, validation, composable commands and is simpler and less abstract in nature
Notes
All commands must follow the following structure:
command [..sub-commands] [..positionals] [..options]
The main command
, optionally followed by sub-commands
, then optional positionals
and finally options
(like --foo
).
Options always come last and cannot be between subcommands and positionals.
This consistent structure allows for more relevant autocomplete functionality and predictable options value parsing.