Cli
Lua module to make building command line programs easier
Install / Use
/learn @gustavo-hms/CliREADME
cli
A lua module to build command line interfaces declaratively.
Some examples
To write a program with flags and positional arguments, you can do the following:
local cli = require "cli"
cli.locale "en_US"
cli.program {
"A description of the program",
cli.flag "o,one" {
"The first option",
type = cli.string
},
cli.flag "the-other" {
"The second option",
type = cli.number,
default = 17
},
cli.positional "file" {
"The input file",
type = cli.string
},
function(args)
print(string.format("One: %s.", args.one))
print(string.format("Two plus 1: %d.", args.the_other + 1))
print(string.format("File: %s.", args.file))
end
}
Supposing this is the code for a program called test-cli, running test-cli -o value input.txt will print:
One: value
Two plus 1: 18
File: input.txt
Automatic help messages
In the above example, running test-cli --help or test-cli -h will print:
A description of the program
Usage:
test-cli [options] file
Options and arguments without a default value are mandatory.
Options:
-o, --one <string>
The first option
--the-other <number> (default: 17)
The second option
Arguments:
file
The input file
Automatic data validation
Sticking with the previous example, calling it with test-cli -o value --the-other=dois input.txt will print:
The following errors were found during the program execution:
∙ the option “--the-other” expects a number, but the given value was “dois”
You can run:
teste.lua --help
if you need some help.
Subcommands
Your program can be easily divided into subcommands:
local cli = require "cli"
cli.locale "en_US"
add = cli.command {
"Add all the given numbers",
function(args)
local sum = 0
for _, v in ipairs(args.numbers) do
sum = sum + v
end
print(sum)
end
}
max = cli.command {
"Find the maximum value",
function(args)
print(math.max(table.unpack(args.numbers)))
end
}
all_above = cli.command {
"Prints all numbers above the given value",
cli.flag "c,cutoff" {
"The value above which all numbers are retained",
type = cli.number
},
function(args)
for _, v in ipairs(args.numbers) do
if v > args.cutoff then
print(v)
end
end
end
}
cli.program {
"A program to compute numbers",
cli.positional "numbers" {
"The numbers to operate upon",
type = cli.number,
many = true,
default = {1, 3, 17}
}
}
Supposing this is the code for an hypothetical compute, running compute --help will print:
A program to compute numbers
Usage:
compute add numbers...
Add all the given numbers
compute all-above [options] numbers...
Prints all numbers above the given value
compute max numbers...
Find the maximum value
You can run
compute <command> --help
to get more details about a specific command.
And, then, running compute all-above --help:
Prints all numbers above the given value
Usage:
compute all-above [options] numbers...
Options and arguments without a default value are mandatory.
Options:
-c, --cutoff <number>
The value above which all numbers are retained
Arguments:
numbers... (default: 1 3 17)
The numbers to operate upon
Usage
The start point of the cli module are the functions cli.program and cli.command. Both have the same interface: a table with the following fields (all of them are optionals):
{
A string containing a description (used for help messages),
A sequence of definitions of command line arguments,
A function to be executed
}
The provided function receives as argument a table with all command line arguments filled with their values:
cli.program {
cli.flag "a-number" {
type = cli.number
},
function(args)
print(args.a_number)
end
}
If an argument has hyphens in its name, they are replaced with underscores, as in the above example.
Any value returned by the main function (the one present in the cli.program table) is passed as an additional argument to the commands' functions:
a_command = cli.command {
cli.flag "do-it" {
type = cli.boolean
},
function(args, seventeen, nineteen)
print(args.do_it)
assert(seventeen == 17)
assert(nineteen == 19)
end
}
cli.program {
function()
return 17, 19
end
}
All global variables assined with a cli.command are interpreted as subcommands of the program, and subcommands will be named after these global variables, replacing underscores with hyphens. So, for instance, the above example will generate a command called a-command.
Defining command line arguments
There are two kinds of arguments: flags and positional arguments.
Flags
Flags are defined with cli.flag, passing to it a name and a table describing it:
cli.flag "first-flag" {
"This is the first flag",
type = cli.boolean
}
If the name contains a comma, the word preceding it is interpreted as a short variant of the word after it:
cli.flag "o,output" {}
The description table has the following fields:
- an optional string containing a description;
- a
typekey describing the value this flag accepts. Must be one ofcli.string,cli.numberorcli.boolean. It defaults tocli.string. If it is set tocli.number, the module will try to convert the value given at program invocation to a number and prints an error if it can not succeed; - an optional
defaultkey containing a default value for this flag. Flags without a default value are considered mandatory.
Positional arguments
Positional arguments are defined with cli.positional, passing to it a name and a table describing it:
cli.positional "file" {
"The file to be read",
type = cli.string
}
The description table has the following fields:
- an optional string containing a description;
- a
typekey describing the value this positional accepts. Must be one ofcli.stringorcli.number. It defaults tocli.string. If it is set tocli.number, the module will try to convert the value given at program invocation to a number and prints an error if it can not succeed; - an optional
defaultkey containing a default value for this positional. Positionals without a default value are considered mandatory; - an optional
manykey telling whether this positional can receive many values at once.
The many key is used this way:
cli.positional "files" {
"The files to be edited",
type = cli.string,
many = true,
default = { "README.md", "cli.lua" }
}
Localisation
The function cli.locale can be used to set the locale of help and error messages:
cli.locale "pt_BR"
