Utilities for create CLIs around functions, and creating tools with multiple sub-commands.
Utilities for create CLIs around functions, and creating tools with multiple sub-commands.
(abort & message)
(abort status & message)
Invoked when a tool has a runtime failure. Writes to standard error; identifies the tool name, category (if any) and command name (in bold red) and then writes the remaining message text after a colon and a space, in red.
Each element of message may either be a composed string, or an exception.
Each exception in the message is converted to a string via ex-message
.
If ex-message
returns nil, then the class name of the exception is used.
By default, the exit status is 1. If the first message value is a number, it is used as the exit status instead.
abort
assumes that the command function was invoked by dispatch
.
When it is invoked otherwise, including when using defcommand
to
create a main entry point, the prefix (normally identifying the tool name and possibly
nested command name, and the colon) are omitted. Just the message portion
is output, in red.
Invoked when a tool has a runtime failure. Writes to standard error; identifies the tool name, category (if any) and command name (in bold red) and then writes the remaining message text after a colon and a space, in red. Each element of message may either be a composed string, or an exception. Each exception in the message is converted to a string via `ex-message`. If `ex-message` returns nil, then the class name of the exception is used. By default, the exit status is 1. If the first message value is a number, it is used as the exit status instead. `abort` assumes that the command function was invoked by `dispatch`. When it is invoked otherwise, including when using `defcommand` to create a main entry point, the prefix (normally identifying the tool name and possibly nested command name, and the colon) are omitted. Just the message portion is output, in red.
(ask prompt responses)
(ask prompt responses opts)
Ask the user a question with a fixed number of possible responses.
The prompt is a string (possibly, a composed string) and should usually end with a question mark.
Each response is a map with
Key | Type | Value |
---|---|---|
:label | String | Response entered by user, e.g., "yes" |
:value | any | Value to be returned by ask , e.g., true |
A response may be a keyword; the :value will be the keyword, and the label will simply be the name of the keyword.
Ex:
(ask "Are you sure?" cli/yes-or-no {:default true})
Will prompt with:
Are you sure? (yes/no)
With "yes" in bold.
The prompt is written to *err*
.
The :value is typically unique, but this is not enforced, and it can be useful to have distinct labels map to the same output value.
The user is allowed to enter a shorter input, if that shorter input
(via best-match
) uniquely identifies a label.
Options:
:default - the default value which must correspond to one value (may not be nil) :force? - if true, then the user is not prompted and the default (which must be non-nil) is returned
The default, if any, is returned when the user simply hits enter (enters a blank string).
The user input must correspond to a label; if not, a warning is printed and the user is again prompted.
Once a label is identified, ask
returns the corresponding value.
Ask the user a question with a fixed number of possible responses. The prompt is a string (possibly, a composed string) and should usually end with a question mark. Each response is a map with Key | Type | Value --- |--- |--- :label | String | Response entered by user, e.g., "yes" :value | any | Value to be returned by `ask`, e.g., true A response may be a keyword; the :value will be the keyword, and the label will simply be the name of the keyword. Ex: (ask "Are you sure?" cli/yes-or-no {:default true}) Will prompt with: Are you sure? (yes/no) With "yes" in bold. The prompt is written to `*err*`. The :value is typically unique, but this is not enforced, and it can be useful to have distinct labels map to the same output value. The user is allowed to enter a shorter input, if that shorter input (via [[best-match]]) uniquely identifies a label. Options: :default - the default value which must correspond to one value (may not be nil) :force? - if true, then the user is not prompted and the default (which must be non-nil) is returned The default, if any, is returned when the user simply hits enter (enters a blank string). The user input must correspond to a label; if not, a warning is printed and the user is again prompted. Once a label is identified, `ask` returns the corresponding value.
(best-match input values)
Given an input string and a seq of possible values, returns the matching value if it can be uniquely identified.
Values may be strings, symbols, or keywords.
best-match does a caseless substring match against the provided values. It returns the single value that matches the input. It returns nil if no value matches, or if multiple values match.
Some special handling for the -
character; the input value is split on -
and turned into
a generous regular expression that matches the substring on either side of the -
as well as the -
itself.
Returns the string/symbol/keyword from values.
e.g. :parse-fn #(cli-tools/best-match % #{:red :green :blue})
would parse an input of red
to
:red
, or an input of b
to :blue
; z
matches nothing and returns nil, as would
e
which matches multiple values.
Expects symbols and keywords to be unqualified.
Given an input string and a seq of possible values, returns the matching value if it can be uniquely identified. Values may be strings, symbols, or keywords. best-match does a caseless substring match against the provided values. It returns the single value that matches the input. It returns nil if no value matches, or if multiple values match. Some special handling for the `-` character; the input value is split on `-` and turned into a generous regular expression that matches the substring on either side of the `-` as well as the `-` itself. Returns the string/symbol/keyword from values. e.g. `:parse-fn #(cli-tools/best-match % #{:red :green :blue})` would parse an input of `red` to `:red`, or an input of `b` to `:blue`; `z` matches nothing and returns nil, as would `e` which matches multiple values. Expects symbols and keywords to be unqualified.
(defcommand fn-name docstring interface & body)
Defines a command.
A command's interface identifies how to parse command options and positional arguments, mapping them to local symbols.
Commands must always have a docstring; this is part of the -h
/ --help
summary.
The returned function is variadic, accepting a number of strings, much
like a -main
function. For testing purposes, it may instead be passed a single map,
a map of options, which bypasses parsing and validation of the arguments.
Finally, the body is evaluated inside a let that destructures the options and positional arguments into local symbols.
Defines a command. A command's _interface_ identifies how to parse command options and positional arguments, mapping them to local symbols. Commands must always have a docstring; this is part of the `-h` / `--help` summary. The returned function is variadic, accepting a number of strings, much like a `-main` function. For testing purposes, it may instead be passed a single map, a map of options, which bypasses parsing and validation of the arguments. Finally, the body is evaluated inside a let that destructures the options and positional arguments into local symbols.
(dispatch options)
Locates commands in namespaces, finds the current command (as identified by the first command line argument) and processes CLI options and arguments.
options:
*command-line-args*
)The :tool-name option is only semi-optional; in a Babashka script, it will default
from the babashka.file
system property, if any. An exception is thrown if :tool-name
is not provided and can't be defaulted.
The default for :tool-doc is the docstring of the first namespace.
dispatch will load any namespaces specified, then scan those namespaces to identify commands.
It also adds a help
command from the net.lewisship.cli-tools namespace.
If option and argument parsing is unsuccessful, then an error message is written to *err*, and the program exits with error code 1.
dispatch simply loads and scans the namespaces (or obtains the necessary data from the
cache), adds the help
command, and finally calls dispatch*
.
Returns nil.
Locates commands in namespaces, finds the current command (as identified by the first command line argument) and processes CLI options and arguments. options: - :tool-name (optional, string) - used in command summary and errors - :tool-doc (optional, string) - used in help summary - :arguments - command line arguments to parse (defaults to `*command-line-args*`) - :namespaces - symbols identifying namespaces to search for commands - :flat (optional, boolean) - if true, then the default help will be flat (no categories) The :tool-name option is only semi-optional; in a Babashka script, it will default from the `babashka.file` system property, if any. An exception is thrown if :tool-name is not provided and can't be defaulted. The default for :tool-doc is the docstring of the first namespace. dispatch will load any namespaces specified, then scan those namespaces to identify commands. It also adds a `help` command from the net.lewisship.cli-tools namespace. If option and argument parsing is unsuccessful, then an error message is written to \*err\*, and the program exits with error code 1. dispatch simply loads and scans the namespaces (or obtains the necessary data from the cache), adds the `help` command, and finally calls [[dispatch*]]. Returns nil.
(dispatch* options)
Invoked by dispatch
after namespace and command resolution.
This can be used, for example, to avoid including the builtin help command (or when providing an override).
options:
locate-commands
)locate-commands
)Each namespace forms a command category, represented as a map with keys:
In the help
command summary, the categories are sorted into ascending order by :order,
then by :label. Individual commands are listed under each category, in ascending alphabetic order.
All options are required.
Returns nil (if it returns at all, as most command will ultimately invoke exit
).
Invoked by [[dispatch]] after namespace and command resolution. This can be used, for example, to avoid including the builtin help command (or when providing an override). options: - :tool-name - used in command summary and errors - :tool-doc - used in command summary - :arguments - seq of strings; first is name of command, rest passed to command - :categories - seq of maps describing the command categories (see [[locate-commands]]) - :commands - seq of command maps (see [[locate-commands]]) Each namespace forms a command category, represented as a map with keys: - :category - symbol identifying the namespace - :command-group string - optional, from :command-group metadata on namespace, groups commands with a prefix name - :label - string (from :command-category metadata on namespace), defaults to the namespace name - :order - number (from :command-category-order metadata on namespace), defaults to 0 In the `help` command summary, the categories are sorted into ascending order by :order, then by :label. Individual commands are listed under each category, in ascending alphabetic order. All options are required. Returns nil (if it returns at all, as most command will ultimately invoke [[exit]]).
(exit status)
An indirect call to System/exit, passing a numeric status code (0 for success, non-zero for an error).
This is provided so that, during testing, when set-prevent-exit!
has been called, the call
to exit
will instead throw an exception.
An indirect call to System/exit, passing a numeric status code (0 for success, non-zero for an error). This is provided so that, during testing, when [[set-prevent-exit!]] has been called, the call to `exit` will instead throw an exception.
(expand-dispatch-options options)
Called by dispatch
to expand the options before calling dispatch*
.
Some applications may call this instead of dispatch
, modify the results, and then
invoke dispatch*
.
Called by [[dispatch]] to expand the options before calling [[dispatch*]]. Some applications may call this instead of `dispatch`, modify the results, and then invoke `dispatch*`.
(locate-commands namespace-symbols)
Passed a seq of symbols identifying loaded namespaces, this function
locates commands, functions defined by defcommand
.
Normally, this is called from dispatch
and is only needed when calling dispatch*
directly.
Returns a tuple: the command categories map, and the command map.
Passed a seq of symbols identifying *loaded* namespaces, this function locates commands, functions defined by [[defcommand]]. Normally, this is called from [[dispatch]] and is only needed when calling [[dispatch*]] directly. Returns a tuple: the command categories map, and the command map.
(print-errors errors)
(print-errors _command-map errors)
Prints the errors for the command to *err*
.
~To invoke this, you need the command map, which is available via the :as clause to defcommand
.~
Ex:
Error in my-tool my-command: --count is not a number
errors is a seq of strings (or composed strings) to display as errors.
In 0.15.1, the two-argument variant was deprecated in favor of the new version which only requires the seq of errors.
Prints the errors for the command to `*err*`. ~To invoke this, you need the command map, which is available via the :as clause to [[defcommand]].~ Ex: Error in my-tool my-command: --count is not a number errors is a seq of strings (or composed strings) to display as errors. In 0.15.1, the two-argument variant was deprecated in favor of the new version which only requires the seq of errors.
(select-option short-opt
long-opt
desc-prefix
input-values
&
{:keys [default] :as kvs})
Builds a standard option spec for selecting from a list of possible values.
Uses best-match
to parse the user-supplied value (allowing for
reasonable abbeviations).
Following the input values is a list of key value pairs; the :default key, if non-nil, should be a member of input-values and will generate :default and :default-desc keys in the option spec.
Adds :parse-fn and :validate keys to the returned option spec, as well as :default and :default-desc. Additional key/value pairs are passed through as-is.
Usage (as part of a command's interface):
... format (select-option
"-f" "--format FORMAT" "Output format:"
#{:plain :csv :tsv :json :edn}) ...
Builds a standard option spec for selecting from a list of possible values. Uses [[best-match]] to parse the user-supplied value (allowing for reasonable abbeviations). Following the input values is a list of key value pairs; the :default key, if non-nil, should be a member of input-values and will generate :default and :default-desc keys in the option spec. Adds :parse-fn and :validate keys to the returned option spec, as well as :default and :default-desc. Additional key/value pairs are passed through as-is. Usage (as part of a command's interface): ``` ... format (select-option "-f" "--format FORMAT" "Output format:" #{:plain :csv :tsv :json :edn}) ...
(set-prevent-exit! flag)
cli-tools will call exit
when help is requested (with a 0 exit status, or 1 for
a input validation error). Normally, that results in a call to System/exit, but this function,
used for testing, allow exit
to throw an exception instead.
cli-tools will call [[exit]] when help is requested (with a 0 exit status, or 1 for a input validation error). Normally, that results in a call to System/exit, but this function, used for testing, allow [[exit]] to throw an exception instead.
(sorted-name-list values)
Converts a seq of strings, keywords, or symbols (as used with best-match
) to a comma-separated
string listing the values. This is often used with help summary or error messages.
Converts a seq of strings, keywords, or symbols (as used with [[best-match]]) to a comma-separated string listing the values. This is often used with help summary or error messages.
For use with ask
, provides responses 'yes' (true) and 'no' (false).
For use with [[ask]], provides responses 'yes' (true) and 'no' (false).
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close