项目地址:https://github.com/kingwel-xie/xcli-rsnode
xcli是一个命令行的工具,支持自定义添加命令,每一个命令支持缩写使用,同时也支持tab方式补全命令。git
这个工具的设计初衷是为了可以提供命令行功能,同时能够很容易的添加自定义的命令。github
目前在libp2p-rs中,xcli提供了命令行的功能,可对swarm和kad进行调试。数组
从命令行中获取到的参数args是一个引用类型的&str数组,即&[&str]。在xcli中,实现了一个名为check_param的宏,返回的值即为想要转换的对应类型。check_param!须要四个参数安全
($param_count:expr, $required:expr, $args:ident, ($($change_type:ty=>$has_from:expr), *))
分别表明参数总个数、必选参数个数,参数列表,最后一个参数比较特殊,表明着须要转换的类型。书写格式形如(String=>1),对于全部输入参数都须要设置该转换类型。网络
须要注意的一点是,参数的总个数必须与最后的参数转换类型个数相同。譬如总共有5个参数,那么后面的类型转换也须要将这五个参数的类型都进行设置。数据结构
举例说明:app
let u = check_param!(3, 1, args, (String=>1, String=>1, String=>1))
这段代码表示总共须要三个参数,其中一个是必须的,另外两个是可选的,这三个参数都是String类型的。返回值的个数最少是1个(必须参数必定返回),最可能是3个。分布式
因为底层库使用的是rustyline,它提供了一个Completer的trait,实现fn complete()便可支持tab补全。ide
在App::run()中,咱们对Command执行了一个方法:
self.rl.borrow_mut().set_helper(Some(PrefixCompleter::new(&self.tree)));
这段代码的逻辑是将Command单独抽离出来造成一个相似树的结构。
如下这段代码是补全功能的核心:
若是知足执行子命令的递归状况,从字符串的偏移量位开始继续执行tab completion.
pub fn _complete_cmd(node: &PrefixNode, line: &str, pos: usize) -> Vec<String> { debug!("cli to complete {} for node {}", line, node.name); let line = line[..pos].trim_start(); let mut go_next = false; let mut new_line: Vec<String> = vec![]; let mut offset: usize = 0; let mut next_node = None; //var lineCompleter PrefixCompleterInterface for child in &node.children { //debug!("try node {}", child.name); if line.len() >= child.name.len() { if line.starts_with(&child.name) { if line.len() == child.name.len() { // add a fack new_line " " new_line.push(" ".to_string()); } else { new_line.push(child.name.to_string()); } offset = child.name.len(); next_node = Some(child); // may go next level go_next = true; } } else if child.name.starts_with(line) { new_line.push(child.name[line.len()..].to_string()); offset = line.len(); next_node = Some(child); } } // more than 1 candidates? if new_line.len() != 1 { debug!("offset={}, candidates={:?}", offset, new_line); return new_line; } if go_next { let line = line[offset..].trim_start(); return PrefixCompleter::_complete_cmd(next_node.unwrap(), line, line.len()); } debug!("offset={}, nl={:?}", offset, new_line); new_line }
以help命令为例,实现一个显示可用命令的功能:
app.add_subcommand( Command::new_with_alias("help", "h") .about("displays help information") .usage("help [command]") .action(cli_help)), ); /// Action of help command fn cli_help(app: &App, args: &[&str]) -> XcliResult { if args.is_empty() { app.tree.show_subcommand_help(); } else if let Some(cmd) = app.tree.locate_subcommand(args) { cmd.show_command_help(); } else { println!("Unrecognized command {:?}", args) } Ok(CmdExeCode::Ok) }
调用add_subcommand()向cli实例中添加一个help命令,action方法参数是一个返回值为XcliResult的fn。XcliResult是一个T为CmdExeCode,E为XcliError的Result类型:
pub type XcliResult = stdResult<CmdExeCode, XcliError>;
在这里咱们定义了cli_help函数,正常运行时返回值为Ok。实现的命令效果如图所示:
add_subcommand_with_userdata()是在v0.5.0新增支持的一个方法。有时候使用者可能但愿测试一些自定义的数据结构,这个方法能够支持用户注册本身的数据到xcli中,后续能够经过命令行的方式进行调试。方法声明以下:
pub fn add_subcommand_with_userdata(&mut self, subcmd: Command<'a>, value: IAny) { self.handlers.insert(subcmd.name.clone(), value); self.tree.subcommands.push(subcmd); }
这段代码的逻辑是将value添加到全局的handler中,handler是一个HashMap,key为命令名称,value是传入的IAny类型值。
方法的第一个参数是Command,定义了命令的名称、子命令、对应的执行函数等等属性;第二个参数是相关的用户数据,IAny是Box<dyn std::any::Any>,意味着能够放入绝大多数的自定义类型参数。
使用的逻辑也较为简单,如下是示例代码:
app.add_subcommand_with_userdata( Command::new_with_alias("userdata", "ud") .about("controls testing features") .action(|app, _args| -> XcliResult { let data_any = app.get_handler("userdata").unwrap(); let data = data_any.downcast_ref::<usize>().expect("usize"); println!("userdata = {}", data); Ok(CmdExeCode::Ok) }), Box::new(100usize) );
在这里,咱们注册了一个叫userdata的子命令,其中value设置为了100。执行userdata命令时,从handler中取出userdata对应的值,downcast_ref解析出usize,再进行println。实现效果如图所示:
因为userdata命令的存在,咱们可使用本身的数据去定义子命令。例如在libp2p-rs中,提供有swarm和kad的controller与主循环交互,所以咱们能够用这两个controller去定义命令:
app.add_subcommand_with_userdata(swarm_cli_commands(), Box::new(swarm_control.clone())); app.add_subcommand_with_userdata(dht_cli_commands(), Box::new(kad_control.clone()));
实现效果图:
swarm peer,无参即展现当前链接peer
swarm peer,有参显示对应peer信息
Netwarps 由国内资深的云计算和分布式技术开发团队组成,该团队在金融、电力、通讯及互联网行业有很是丰富的落地经验。Netwarps 目前在深圳、北京均设立了研发中心,团队规模30+,其中大部分为具有十年以上开发经验的技术人员,分别来自互联网、金融、云计算、区块链以及科研机构等专业领域。Netwarps 专一于安全存储技术产品的研发与应用,主要产品有去中心化文件系统(DFS)、去中心化计算平台(DCP),致力于提供基于去中心化网络技术实现的分布式存储和分布式计算平台,具备高可用、低功耗和低网络的技术特色,适用于物联网、工业互联网等场景。公众号:Netwarps