9 个版本
0.3.3 | 2023 年 8 月 20 日 |
---|---|
0.3.2 | 2023 年 8 月 1 日 |
0.3.0 | 2023 年 7 月 31 日 |
0.2.1 | 2023 年 7 月 31 日 |
0.1.1 | 2023 年 7 月 28 日 |
#277 in 命令行界面
71KB
2K SLoC
ClapCmd
一个库,可快速构建由 CLAP 和 readline(通过 rustyline 提供)支持的完整功能的 REPL
功能
- 完全支持 readline,并公开 rustyline 的所有自定义选项
- 默认为 emacs 风格的键盘快捷键(可通过 rustyline 自定义)
- 命令历史(内存缓冲区中)
- 与 clap 构建器完全集成,允许使用完整功能的命令
- Tab 完成支持
- 命令和(待办)命令别名
- 参数
- 子命令
- 通过
value_parsers
(即有效值列表)提供的值 - 值提示(例如
ValueHint::FilePath
) - 待办:回调和/或演示如何查询
value_parsers
的方法
- 具有提供状态的回调样式方法
- 可自定义的提示,可以在执行期间随时更新
- 支持通过
get_async_writer()
在命令循环外写入 stdout,而不会破坏输入行 - 创建可加载和卸载的命令组
- 通过行尾的 '\' 字符支持多行输入
- 通过
- 分号(
;
)进行无条件评估 - 双 ampersand(
&&
)用于链式成功评估 - 双 pipe(
||
)用于错误处理评估
- 分号(
- 通过
test-runner
功能进行自动测试
基本示例
以下是一个展示基本 REPL 的最小示例
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();
}
带有状态
要将状态或持久信息传递给回调,提供类似于以下所示的 State
类。该 State
类必须实现 Clone
特性,并通过 ClapCmd
引用上的 get_state()
和 set_state()
方法进行访问。
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();
}
使用组
组可以用于在内置的 help
菜单中逻辑上分离命令集合。它们也可以通过 add_group
和 remove_group
方法快速激活和停用命令。
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();
}
端到端测试
通过启用 test-runner
功能和使用内置的 output
、success
、info
、warn
和 error
函数,可以轻松自动化 CLI 的端到端测试。有关更多示例,请参阅 tests/
文件夹。
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",
);
其他示例
有关高级用例的更多演示,请参阅 examples/
文件夹。
MSRV
此库与 Rust 1.65 及其最新版本一起进行了测试。
相关项目
- reedline-repl-rs https://github.com/arturh85/reedline-repl-rs
依赖项
~4.5MB
~79K SLoC