Crates.io | clapcmd |
lib.rs | clapcmd |
version | 0.3.3 |
source | src |
created_at | 2023-07-28 01:04:21.526826 |
updated_at | 2023-08-20 08:58:58.908851 |
description | A readline wrapper that allows for creating custom interactive shells, similar to python's cmd module |
homepage | |
repository | https://gitlab.com/clapcmd/clapcmd |
max_upload_size | |
id | 928059 |
size | 103,042 |
A library to quickly build full-featured REPLs supported by CLAP and readline (provided via rustyline)
value_parsers
(i.e. a list of valid values)ValueHint::FilePath
)value_parsers
at runtimeget_async_writer()
;
) for unconditional evaluation&&
) for chaining successful evaluations||
) for error handling evaluationstest-runner
featureA minimal example showing a basic REPL is as follows:
use clapcmd::{ArgMatches, ClapCmd, ClapCmdResult, Command};
fn do_ping(cmd: &mut ClapCmd, _: ArgMatches) -> ClapCmdResult {
cmd.output("pong");
Ok(())
}
fn main() {
let mut cmd = ClapCmd::default();
cmd.add_command(
do_ping,
Command::new("ping").about("do a ping")
);
cmd.run_loop();
}
To pass state or persistent information to callbacks, provide a State
class like so.
The State
class must implement Clone
trait, and can be accessed via
the get_state()
and set_state()
methods on the ClapCmd
reference passed into the
callback.
use clapcmd::{ArgMatches, ClapCmd, ClapCmdResult, Command};
#[derive(Clone)]
struct State {
counter: u32,
}
fn do_count(cmd: &mut ClapCmd<State>, _: ArgMatches) -> ClapCmdResult {
let state = cmd.get_state().ok_or("state missing")?;
let new_count = state.counter + 1;
cmd.info(format!("the count is now: {}", new_count));
cmd.set_state(State { counter: new_count });
Ok(())
}
fn main() {
let mut cmd = ClapCmd::with_state(State { counter: 0 });
cmd.add_command(do_count, Command::new("count").about("increment a counter"));
cmd.run_loop();
}
Groups can be used to logically separate sets of commands in the built-in help
menu.
They can also be used to quickly activate and deactivate commands via the add_group
and
remove_group
methods
use clapcmd::{ArgMatches, ClapCmd, ClapCmdResult, Command, HandlerGroup};
use once_cell::sync::Lazy;
static LOADED_GROUP: Lazy<HandlerGroup> = Lazy::new(|| {
ClapCmd::group("Fruit")
.description("Commands to do cool fruit things")
.command(
do_apple,
Command::new("apple").about("do the cool apple thing"),
)
.command(
do_banana,
Command::new("banana").about("do the cool banana thing"),
)
.command(
do_unload,
Command::new("unload").about("unload the cool fruit group"),
)
});
static UNLOADED_GROUP: Lazy<HandlerGroup> = Lazy::new(|| {
ClapCmd::unnamed_group().command(
do_load,
Command::new("load").about("load the cool fruit group"),
)
});
fn do_load(cmd: &mut ClapCmd, _: ArgMatches) -> ClapCmdResult {
cmd.add_group(&LOADED_GROUP);
cmd.remove_group(&UNLOADED_GROUP);
cmd.info("loaded");
Ok(())
}
fn do_unload(cmd: &mut ClapCmd, _: ArgMatches) -> ClapCmdResult {
cmd.add_group(&UNLOADED_GROUP);
cmd.remove_group(&LOADED_GROUP);
cmd.info("unloaded");
Ok(())
}
fn do_apple(cmd: &mut ClapCmd, _: ArgMatches) -> ClapCmdResult {
cmd.output("apple");
Ok(())
}
fn do_banana(cmd: &mut ClapCmd, _: ArgMatches) -> ClapCmdResult {
cmd.output("banana");
Ok(())
}
fn main() {
let mut cmd = ClapCmd::default();
cmd.add_group(&UNLOADED_GROUP);
cmd.run_loop();
}
By enabling the test-runner
feature and using the built-in output
, success
, info
, warn
, and error
functions, it is easy to automate e2e tests of your CLI. See the tests/
folder for more examples.
use clapcmd::{ArgMatches, ClapCmd, ClapCmdResult, Command};
fn do_hello(cmd: &mut ClapCmd, _: ArgMatches) -> ClapCmdResult {
cmd.output("hello");
Ok(())
}
let mut cmd = ClapCmd::default();
cmd.add_command(
do_hello,
Command::new("hello").about("simple hello world")
);
let _ = cmd.one_cmd("goodbye");
#[cfg(feature = "test-runner")]
assert!(
cmd.error.contains("unknown command"),
"did not detect invalid command",
);
let _ = cmd.one_cmd("hello");
#[cfg(feature = "test-runner")]
assert!(
cmd.output.contains("hello"),
"did not run hello world command correctly",
);
Refer to the examples/
folder for more demonstrations of advanced use cases
This library is tested with Rust 1.65 along with the latest version of Rust