Create a simple command-line interface (CLI) application with Lua. It's inspired by JavaScript's commander.
- Features
- Installation
- Usage
- Create an instance
- Add commands
- Command examples
- Parse input
- Lummander instance methods
- Theme
- Add the CLI to the PATH
- Define required positional arguments, optional or flagged options for a command.
- Add commands directly to lummander instance or from a folder.
Require LuaFileSystem.
- Using Luarocks:
luarocks install lummander
-- Require "lummander"
local Lummander = require "lummander"
-- Create a lummander instance
local cli = Lummander.new{
title = "My Custom App", -- <string> title for CLI. Default: ""
tag = "myapp", -- <string> CLI Command to execute your program. Default: "".
description = "My App description", -- <string> CLI description. Default: ""
version = "0.1.1", -- <string> CLI version. Default: "0.1.0"
author = "Myself", -- <string> author. Default: ""
root_path = "/path/to/folder/contains/this/file", -- <string> root_path. Default "". Concat this path to load commands of a subfolder
theme = "acid", -- Default = "default". "default" and "acid" are built-in themes
flag_prevent_help = false -- <boolean> Prevent help message if not command found. Default: false
}
-- Add commands
cli:command("mycmd", "My command description")
:action(function(parsed, command, app)
print("You activated `mycmd` command")
end)
cli:command("sum <value1> <value2>", "Sum 2 values")
:option(
"option1","o","Option1 description",nil,"normal","option_default_value")
:option(
"option2","p","Option2 description",nil,"normal","option2_default_value")
:action(function(parsed, command, app)
print("".. parsed.value1.. "+"..parsed.value2.." = " ..
tostring(tonumber(parsed.value1) + tonumber(parsed.value2)))
end)
-- Parse and execute the command wrote
cli:parse(arg) -- parse arg and execute if a command was written
local Lummander = require"lummander"
local cli = Lummander.new{
title = "CLI title",
tag = "myapp", -- define command to launch this script at terminal
description = "My App description", -- <string> CLI description. Default: ""
version = "0.1.1", -- define cli version
author = "Myself", -- <string> author. Default: ""
root_path = "/path/to/folder/contains/this/file", -- <string> root_path. Default "". Concat this path to load commands of a subfolder
theme = "acid", -- Default = "default". "default" and "acid" are built-in themes
flag_prevent_help = false -- prevent show help when :parse() doesn't find a valid command to execute
}
Ways to add commands: a. Using lummander instance methods b. Loading from a folder files what contain a command table defined in lua files.
Create a command with:
cli:command(schema, description, config)
-- or
cli:command(schema, config)
-- or
cli:command(config)
- schema: string - command schema
- description: string or table - command description
- config: table or nil - command config. Table with fields
Note: if description is a table, then is treated like config.
It's a string what should have a main command word. It can have required positional arguments (<>) or optionals ([]).
Example: mycmd <required_positional_argument1> <required_positional_argument2> [optional_positional_argument1]
Arguments:
- <argument_name> - Required argument
- [argument_name] - Optional argument
- [argument_name...] - Optional arguments (array)
- [...argument_name] - Optional arguments (array)
cmd:option(...)
: add a flagged option.cmd:action(fn)
: set a function to execute for this command.
See docs to see more methods.
Add options to a command with:
-- only long_name/long and short_name/short are required
cmd:option(long_name, short_name, description, transform, type, default)
-- or
cmd:option({long, short, description, transform, type, default})
- short_name: string - option short name
- long_name: string - option long name
- description: string - option description
- transform: function - transform value received for this option before execute action
- type:
normal
orflag
. Defaultnormal
.flag
is true or false.normal
can accept a value - default: option default value (setted by default to
parsed
table)
Example:
cli:command("mycmd","My cmd description")
:option("output", "o", "My flagged option description", function(value) return "./"..value..".txt" end, "normal", "my_default_value")
-- You can add multiple options
cli:command("mycmd","My cmd description")
:option("output", "o", "My flagged option description", function(value) return "./"..value..".txt" end, "normal", "my_default_value")
:option("mode", "m", "Option mode description")
:option({long = "confirm", short = "c", description = "No require confirm", type = "flag"}) -- addig with table as first argument
You can add so many flagged options like you want.
You can define too this config in a table when you create the command. The example from above would be:
cli:command("mycmd","My cmd description", {
options = {
{long = "output", short = "o", description = "My flagged option description", transform = function(value) return "./"..value..".txt" end, type = "normal", default = "my_defautl_value"}
}
})
cli:command("mycmd","My cmd description", {
options = {
{long = "output", short = "o", description = "My flagged option description", transform = function(value) return "./"..value..".txt" end, type = "normal", default = "my_defautl_value"},
{long = "mode", short = "m", description = "Option mode description"}
}
})
-- You can pass command config table in description argument and add to table the description field
cli:command("mycmd", {
description = "My cmd description", -- includes in command config argument
options = {
{long = "output", short = "o", description = "My flagged option description", transform = function(value) return "./"..value..".txt" end, type = "normal", default = "my_defautl_value"}
}
})
Set an action to execute when the command is activated.
cmd:action(fn)
fn
function - trigger a function when command is activated.
cmd:action(function(parsed, command, app)
-- parsed: table with command parsed. Include required positional arguments, optional positional arguments, and flagged options (with long name)
-- command: command itself
-- app: lummander instance
end)
You can print parsed table with parsed:print()
to see values that contains.
You can add commands from a folder with .lua files.
cli:commands_dir("folderpath")
The command file interface is:
-- folderpath/mycmd.lua
return {
schema = "mycmd <req_arg> [opt_arg]", -- Schema to parse. Required
description = "Command description", -- Command description
positional_args = { -- Set description or {description, default} for positional arguments
req_arg = "A description for a required argument",
opt_arg = {description = "A description for a optional argument", default = "default_positional_option_value"},
},
options = { -- Add flags arguments
{long = "yes", short = "y", description = "Accept", transform : function(param) end, type = "normal", default = "default_value"} -- same command:option("y","yes", "Accept", function(param) end, "normal", "default_value")
},
hide = false, -- hide from help command
main = true, -- do this command default action to CLI if true. Default = nil = false
action = function(parsed, command, app) -- same command:action(function)
parsed:print()
local sufix = ""
if(parsed.yes)then sufix = " -y" end
os.execute("npm init"..sufix)
end
}
Note: only schema
is required
-- Command with only a command word.
-- Argumments:
-- - cmd: time
cli:command("time", "Show time")
:action( -- Join a action to execute
function(parsed, command, app) -- app is lummander instance
print(os.date("Time is: %I:%M:%S"))
end
)
-- Command with only a command word and a required positional argument.
-- Argumments:
-- - cmd: hi
-- - req_arg1: name (closed in <> means is required)
cli:command("hi <name>", "Say hi to someone")
:action(
function(parsed, command, app)
-- parsed is a table that includes a field called "name" due to <name> at command schema
-- <name> is a required positional argument and is needed to trigger this function
-- parsed = { name }
print("Hi " .. parsed.name)
end
)
-- Command with only a command word, a required positional argument and an option.
-- Argumments:
-- - cmd: hi
-- - rea_arg1: name (closed in <> means is required)
-- - option: hello/h
cli:command("hi <name>", "Say hello/hi to someone")
:option("hello", "h","Say hello instead") -- include a option. Example: myapp hi MyName -h. Then parsed.hello = true
:action(
function(parsed, command, app)
-- parsed = {name, hello}
local saludation = "Hi"
if(parsed.hello) then saludation = "Hello" end
print(saludation .. " " .. parsed.name)
-- `hi Lummander` => Hi Lummander
-- `hi Lummander -h` => Hello Lummander
-- `hi Lummander --hello` => Hello Lummander
end
)
-- Command with a command word, 1 required positional argument, 1 optional positional and 1 option
-- Argumments:
-- - cmd: hi
-- - req_arg1: text (closed in <> means is required)
-- - opt_arg2: othertext (closed in [] means is optional)
-- - option: -o, --output
cli:command("save <text> [othertext]", "Save a file")
:option("output", "o", "Output path")
:action(
function(parsed)
-- parsed = {text, othertext, output}
local default_path = "mypath/file.txt"
local filename = parsed.output or default_path
my_write_file_function(filename, parsed.text)
-- `save "My text"` => Save a file at default_path
-- `save "My text" -o otherpath/myotherfile.txt` => Save a file at "otherpath/myotherfile.txt"
-- `save "My text" --output otherpath/myotherfile.txt` => Save a file at "otherpath/myotherfile.txt"
end
)
cli:command("install [packs...]")
:option("dev", "d", "Set install mode", nil, "flag")
:action(function(parsed, command, app)
parsed.packs:for_each(function(pack) -- options like array is a table with special methods. See https://desvelao.github.io/f/classes/ftable.html
-- do something with each pack
end)
end)
cli:parse(arg) -- Parse a table like-array (space/comilla separated arguments). `arg` variable in Lua is an array that contains arguments passed when it executed. This is REQUIRED. Execute a command if is found.
-- cli.parsed property is created after this
Note: cli
is lummander instance.
cli.log:info(...)
: info logcli.log:warn(...)
: warning logcli.log:error(...)
: error log
cli:execute(cmd, [fn])
: like os.execute but returns text or callback function with value returned by cmd executed.
cli:execute("cd", function(value)
-- do something with value after terminal executution is finished
end)
-- same to:
local value = cli:execute("cd")
-- do something with value or after terminal executution is finished
-
cli:find_cmd(cmd_name)
: find a cli command by name -
cli:action(cmd_name)
: set default action. String or Command. Default (help command) -
cli:apply_theme(theme)
: apply a theme -
cli.pcall(fn)
: execute a function as pcall() -
cli.lfs
: access to LuaFileSystem
pcall(fn)
: create a pcall instance with a function to check.pcall:pass(fn)
: function to execute if pcall success.pcall:fail(fn)
: function to execute if pcall failed. Call pcall:done().pcall:done()
: execute the pcall
lum.pcall(function() -- check function to use in pcall
print("hi")
error("Error found on this function")
end)
:pass(function() -- execute if check function doesn't raise errors
-- code..
end):fail(function() -- execute if chech function raise some error
-- code...
end)
You can can create a custom theme for your terminal. Styles that you can apply are:
- text color:
black
,red
,green
,yellow
,blue
,magenta
,cyan
,white
- background color:
bgblack
,bgred
,bggreen
,bgyellow
,bgblue
,bgmagenta
,bgcyan
,bgwhite
- other:
bold
,underlined
andreversed
Use cli:apply_theme(theme)
to load it.
Example:
-- mytheme.lua
return {
cli = {
title = "yellow",
text = "white",
category = "yellow"
},
command = {
definition = "green",
description = "white",
argument = "yellow",
option = "yellow",
category = "red"
},
primary = "green",
secondary = "red",
success = "green",
warning = "yellow",
error = "red"
}
Note: if you dont define some style, this will be white
by default.
cli.theme
print with theme color defined the text
cli.theme.cli.title(text)
cli.theme.cli.text(text)
cli.theme.cli.category(text)
cli.theme.command.definition(text)
cli.theme.command.description(text)
cli.theme.command.argument(text)
cli.theme.command.option(text)
cli.theme.command.category(text)
cli.theme.primary(text)
cli.theme.secondary(text)
cli.theme.sucess(text)
cli.theme.warning(text)
cli.theme.error(text)
Create a .bat
or .cmd
file what contains:
lua "absolute_path_to_your_cli_script.lua" %*
The name of this file will be CLI command to init your CLI script.
Note: lua.exe
should be in a folder what is in os PATH variable, if not, add the folder path to os PATH variable.
Example:
lua "C:/path_to_cli/mycli.lua" %*
MIT