文盘Rust——领域交互模式如何实现
京东云技术团队
- 关注
文盘Rust——领域交互模式如何实现
作者:京东云 贾世闻上回说到如何通过interactcli-rs四步实现一个命令行程序。但是shell交互模式在有些场景下用户体验并不是很好。比如我们要连接某个服务,比如mysql或者redis这样的服务。如果每次交互都需要输入地址、端口、用户名等信息,交互起来太麻烦。通常的做法是一次性输入和连接相关的信息或者由统一配置文件进行管理,然后进入领域交互模式,所有的命令和反馈都和该领域相关。interactcli-rs通过 -i 参数实现领域交互模式。这回我们探索一下这一模式是如何实现的。基本原理interactcli-rs 实现领域交互模式主要是循环解析输入的每一行,通过rustyline 解析输入的每一行命令,并交由命令解析函数处理响应逻辑当我们调用 ‘-i’ 参数的时候 实际上是执行了 interact::run() 函数(interact -> cli -> run())。
pub fn run() {
let config = Config::builder()
.history_ignore_space(true)
.completion_type(CompletionType::List)
.output_stream(OutputStreamType::Stdout)
.build();
let h = MyHelper {
completer: get_command_completer(),
highlighter: MatchingBracketHighlighter::new(),
hinter: HistoryHinter {},
colored_prompt: "".to_owned(),
validator: MatchingBracketValidator::new(),
};
let mut rl = Editor::with_config(config);
rl.set_helper(Some(h));
if rl.load_history("/tmp/history").is_err() {
println!("No previous history.");
}
loop {
let p = format!("{}> ", "interact-rs");
rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
let readline = rl.readline(&p);
match readline {
Ok(line) => {
if line.trim_start().is_empty() {
continue;
}
rl.add_history_entry(line.as_str());
match split(line.as_str()).as_mut() {
Ok(arg) => {
if arg[0] == "exit" {
println!("bye!");
break;
}
arg.insert(0, "clisample".to_string());
run_from(arg.to_vec())
}
Err(err) => {
println!("{}", err)
}
}
}
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
}
}
rl.append_history("/tmp/history")
.map_err(|err| error!("{}", err))
.ok();
}
解析主逻辑交互逻辑主要集中在 ‘loop’ 循环中,每次循环处理一次输入请求。处理的逻辑如下:定义提示符,类似 'mysql> ',提示用户正在使用的程序
let p = format!("{}> ", "interact-rs");
rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p);
读取输入行进行解析
将输入的命令行加入到历史文件,执行过的命令可以通过上下键回放来增强用户体验。
rl.add_history_entry(line.as_str());
将输入的行解析为 arg 字符串,交由 cmd::run_from 函数进行命令解析和执行
match split(line.as_str()).as_mut() {
Ok(arg) => {
if arg[0] == "exit" {
println!("bye!");
break;
}
arg.insert(0, "clisample".to_string());
run_from(arg.to_vec())
}
Err(err) => {
println!("{}", err)
}
}
解析中断,当用户执行 ctrl-c 或 ctrl-d 时,退出程序。
Err(ReadlineError::Interrupted) => {
println!("CTRL-C");
break;
}
Err(ReadlineError::Eof) => {
println!("CTRL-D");
break;
}
Err(err) => {
println!("Error: {:?}", err);
break;
}
run函数中其他代码的作用配置rustyline
let config = Config::builder()
.history_ignore_space(true)
.completion_type(CompletionType::List)
.output_stream(OutputStreamType::Stdout)
.build();
这个config其实是rustyline的配置项,包括输出方式历史记录约束,输出方式等等。MyHelper用于配置命令的autocompletelet h = MyHelper {
completer: get_command_completer(),
highlighter: MatchingBracketHighlighter::new(),
hinter: HistoryHinter {},
colored_prompt: "".to_owned(),
validator: MatchingBracketValidator::new(),
};
这里卖个关子,下期详细讲讲autocomplete的实现。配置历史文件
rl.append_history("/tmp/history")
.map_err(|err| error!("{}", err))
.ok();
关于如何构建命令行的领域交互模式就说到这儿,下期详细介绍一下 autocomplete 如何实现。
免责声明
1.一般免责声明:本文所提供的技术信息仅供参考,不构成任何专业建议。读者应根据自身情况谨慎使用且应遵守《中华人民共和国网络安全法》,作者及发布平台不对因使用本文信息而导致的任何直接或间接责任或损失负责。
2. 适用性声明:文中技术内容可能不适用于所有情况或系统,在实际应用前请充分测试和评估。若因使用不当造成的任何问题,相关方不承担责任。
3. 更新声明:技术发展迅速,文章内容可能存在滞后性。读者需自行判断信息的时效性,因依据过时内容产生的后果,作者及发布平台不承担责任。
本文为 京东云技术团队 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
京东中台化底层支撑框架技术分析及随想
2025-04-08
Web Components实践:如何搭建一个框架无关的AI组件库
2025-04-08
计算机网络协议介绍
2025-04-08