文盘Rust——起手式,CLI程序 | 京东云技术团队

技术的学习从不会到会的过程是最有意思的,也是体会最多的。一旦熟练了,知识变成了常识,可能就失去了记录学习过程的最佳时机。

在我看来学习一门计算机语言和学习人类语言有很多共通之处。我们学习人类语言是从单个的词开始,然后是简单句子,通过不断的与他人交互练习掌握语法和语言习惯。当熟练到一定程度就可以表达思想。计算的语言也差不多,熟悉关键词,基本逻辑,标准库,写应用。只是沟通的对象是机器而已。

既然是学就不能在开始搞的太难。学习本来就是个艰苦的差事。上来就干特别复杂的事情往往会坚持不下去。天下难事必做于易,从简入繁,从易到难,方为正道。

先聊聊最简单的CLI(Command Line Interface)程序。其实我们每学习一门语言的 hello world 程序就是CLI,只是没那么多交互而已。

做命令行程序最繁琐的事情是处理交互。交互大体分两种。一种是我们最熟悉shell下的交互模式,每次一个命令,配合参数实现一次处理返回一组结果。这种模式处理起来比较容易Rust也有相当优秀的第三方lib (clap)。第二种是领域交互,就像我是使用MySql或者redis的客户端程序。这种程序可以玩儿的东西就比较多了像如何实现交互,如何来做子命令的提示。这些东西 clap 并没有提供,需要我们自己来实现。

interactcli-rs是我在工作过程中做的一个交互模式命令行脚手架。实现了一些常用功能。

下面我们来看看如何通过几个步骤快速的实现一个功能相对齐全的CLI程序。和做饭一样,能够快速获得成就感的方式是找半成品直接下锅炒一盘:)。

下面我们具体看看,如何通过interactcli-rs实现一个功能齐全的命令行程序

来点感性认识

先把项目clone下来运行个例子

  • clone 项目

    1git clone https://github.com/jiashiwen/interactcli-rs.git 2cd interactcli-rs 3 4 5 6
  • 命令行模式

    1cargo run requestsample baidu 2 3 4 5
  • 交互模式

    1cargo run -- -i 2interact-rs> requestsample baidu 3 4 5 6

运行上面的命令是通过http来请求百度

四步做个CLI

首先我们先来看看框架的目录结构

1 2. 3├── examples 4├── log 5├── logs 6└── src 7 ├── cmd 8 ├── commons 9 ├── configure 10 ├── interact 11 ├── logger 12 └── request 13 14 15 16

cmd目录是我们做自己功能时要动的主要目录,下面我们一步一步的实现requestsample命令。

  • 定义命令
    cmd 模块用于定义命令以及相关子命令,requestsample.rs 中定义了访问百度的命令

    1use clap::Command; 2 3pub fn new_requestsample_cmd() -> Command<'static> { 4clap::Command::new("requestsample") 5.about("requestsample") 6.subcommand(get_baidu_cmd()) 7} 8 9pub fn get_baidu_cmd() -> Command<'static> { 10clap::Command::new("baidu").about("request www.baidu.com") 11} 12 13 14 15

    new_requestsample_cmd 函数定义了命令 "requestsample",get_baidu_cmd 函数定义了 requestsample 的子命令 baidu

  • 注册命令
    src/cmd/rootcmd.rs 文件中定义了命令树,可以在此注册定义好的子命令

    1lazy_static! { 2 static ref CLIAPP: clap::Command<'static> = clap::Command::new("interact-rs") 3 .version("1.0") 4 .author("Your Name. ") 5 .about("command line sample") 6 .arg_required_else_help(true) 7 .arg( 8 Arg::new("config") 9 .short('c') 10 .long("config") 11 .value_name("FILE") 12 .help("Sets a custom config file") 13 .takes_value(true) 14 ) 15 .arg( 16 Arg::new("daemon") 17 .short('d') 18 .long("daemon") 19 .help("run as daemon") 20 ) 21 .arg( 22 Arg::new("interact") 23 .short('i') 24 .long("interact") 25 .conflicts_with("daemon") 26 .help("run as interact mod") 27 ) 28 .arg( 29 Arg::new("v") 30 .short('v') 31 .multiple_occurrences(true) 32 .takes_value(true) 33 .help("Sets the level of verbosity") 34 ) 35 .subcommand(new_requestsample_cmd()) 36 .subcommand(new_config_cmd()) 37 .subcommand(new_multi_cmd()) 38 .subcommand(new_task_cmd()) 39 .subcommand(new_loop_cmd()) 40 .subcommand( 41 clap::Command::new("test") 42 .about("controls testing features") 43 .version("1.3") 44 .author("Someone E. ") 45 .arg( 46 Arg::new("debug") 47 .short('d') 48 .help("print debug information verbosely") 49 ) 50 ); 51 static ref SUBCMDS: Vec = subcommands(); 52} 53 54pub fn run_app() { 55 let matches = CLIAPP.clone().get_matches(); 56 if let Some(c) = matches.value_of("config") { 57 println!("config path is:{}", c); 58 set_config_file_path(c.to_string()); 59 } 60 set_config(&get_config_file_path()); 61 cmd_match(&matches); 62} 63 64pub fn run_from(args: Vec) { 65 match clap_Command::try_get_matches_from(CLIAPP.to_owned(), args.clone()) { 66 Ok(matches) => { 67 cmd_match(&matches); 68 } 69 Err(err) => { 70 err.print().expect("Error writing Error"); 71 } 72 }; 73} 74 75 76 77 78

    定义好的命令不需其他处理,框架会在系统运行时生成子命令树,用于在领域交互模式下命令提示的支持

  • 命令解析
    src/cmd/rootcmd.rs 中的 cmd_match 负责解析命令,可以把解析逻辑写在该函数中

    1fn cmd_match(matches: &ArgMatches) { 2 if let Some(ref matches) = matches.subcommand_matches("requestsample") { 3 if let Some(_) = matches.subcommand_matches("baidu") { 4 let rt = tokio::runtime::Runtime::new().unwrap(); 5 let async_req = async { 6 let result = req::get_baidu().await; 7 println!("{:?}", result); 8 }; 9 rt.block_on(async_req); 10 }; 11 } 12} 13 14 15 16
  • 修改交互模式的命令提示
    提示符可以在src/interact/cli.rs 中定义

    1pub fn run() { 2 3 ... 4 5 loop { 6 let p = format!("{}> ", "interact-rs"); 7 rl.helper_mut().expect("No helper").colored_prompt = format!("\x1b[1;32m{}\x1b[0m", p); 8 9 ... 10 } 11 12 ... 13} 14 15 16 17 18

先写到这里,下次为大家介绍一下interactcli-rs各种功能是如何实现的。

作者:京东科技 贾世闻

来源:京东云开发者社区 转载请注明来源

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

mysql中like用法

like的通配符有两种%(百分号):代表零个、一个或者多个字符。\(下划线):代表一个数字或者字符。1\.name以"李"开头wherenamelike'李%'2\.name中包含"云",“云”可以在任何位置wherenamelike'%云%'3\.第二个和第三个字符是0的值wheresalarylike'\00%'4\

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.