| Crates.io | dsl_cli |
| lib.rs | dsl_cli |
| version | 0.3.0 |
| created_at | 2026-01-09 08:43:46.700441+00 |
| updated_at | 2026-01-23 07:49:53.920652+00 |
| description | A small proc-macro DSL for defining command-line interfaces. |
| homepage | |
| repository | https://github.com/rarescovei5/dsl_cli |
| max_upload_size | |
| id | 2031802 |
| size | 11,526 |
dsl_cliAdd the dependency in your Cargo.toml:
[dependencies]
dsl_cli = "0.3.0"
Or using cargo:
cargo add dsl_cli
Then define your CLI using the cli! macro:
use dsl_cli::cli;
// The macro expands to items (types + functions), so it can be used at module scope.
cli! {
name "string_utils",
version "0.1.0",
description "A simple CLI for string utilities",
cmd split "Split a string by a separator" {
arg string "The string to split",
req_opt "-s, --separator" "The separator to use" {
arg string
},
},
}
fn main() {
let parsed = parse_env(std::env::args().skip(1).collect());
match parsed {
Command::Split(args, opts) => {
println!(
"{}",
args.string
.split(&opts.separator)
.collect::<Vec<&str>>()
.join(" ")
);
}
}
}
Notes:
help is a built-in command: run <exe> help or <exe> help <command> (trying to override won't lead to anything).cli is a special command see here.pub items, so you can import them from other modules (e.g. use crate::{Command, parse_env, SplitArgs, SplitOpts};).Metadataname - The name of the CLIversion - The version of the CLIdescription - The description of the CLICommandscmd keyword.cmd <name> ["description"] {
...
}
A special kind of command is the cli command which is used to define arguments and options that are used when no command is provided (top-level arguments and options). It's important to note that top-level arguments/options are not global, so we can't define an option in the cli command and use it in another command.
If the cli command has positional arguments and the user supplies the first one and it happens to be the name of a command, the command will be executed instead of the cli command.
In other words: command names always take priority over cli when matching the first token. Avoid using a first positional argument that can collide with your subcommand names.
Argumentsarg keyword.: character. Obviously, we can only supply types that can be parsed from a string.arg <name> ["description"] [: <type>] [= <default>],
We can make an argument optional by supplying an Option<T> type. In this case, we can also provide a default value for the argument by using the = character. Defaults are only allowed for arguments that can be absent; if provided, the generated field type becomes T (not Option<T>).
Absence is dictated by the type and the context in which the argument is defined. For example:
Option<T>, it can be absent.Option<T>.We can make an argument variadic by supplying a Vec<T> type. If we want an optional variadic argument, we can supply an Option<Vec<T>> type AND NOT Vec<Option<T>>.
Optionsopt keyword.-f) or long (--flag) or both (-f, --flag). Flags must be provided as a string literal, e.g. "-f, --flag".opt "<flags>" ["description"] [{
...args
}],
We can make an option required by supplying the req_opt keyword instead of opt. This will make the option required to be provided when the command is used.
The syntax is the same as defining a positional argument, but here we don't allow a description. If you want to convey meaning about something in the option, do it directly in the option description.
The CLI will automatically generate a help message for the commands, arguments and options. The help message will be displayed when the user runs help with or without a command name.
Whenever the CLI encounters an error, it will display what the user did wrong, how to fix it, and suggest running the help command for more information.
MIT License - Copyright (c) 2026 Covei Rares