Rust 写脚手架,Clap你应该知道的二三事( 四 )


Rust 写脚手架,Clap你应该知道的二三事

文章插图
图片
f_cli 选择UI库
那我们就再次用一个简单的例子来介绍一下哇 。
安装新的包首先 , 我们需要安装几个用于交互的包 。
cargo add anyhowcargo add dialoguercargo add console随后 , 就他们就会自动被注入到Cargo.toml中了 。关于anyhow/dialoguer/console我们就不在这里过多介绍了 。大家感兴趣可以去对应的官网查找.
  • dialoguer[6]
  • console[7]
  • anyhow[8]
现在,我们需要在src/main.rs中引入相关的功能,同时我们在处理cli变量的时候,用的是枚举值,所以我们需要引入clap中针对这类的操作 。
use clap::{ +builder::EnumValueParser,Parser,Subcommand, +ValueEnum };+use dialoguer::{ +console::Term, +theme::ColorfulTheme, +Select +};+use console::style;新增枚举信息前面说过,我们想通过人机交互的方式,在cli运行过程中让用户自己选择我们内置的功能点 。所以,这些内置功能我们可以需要事先设定好 。
#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]pub enum Name {N1,N2,}#[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]pub enum Address {A1,A2}处理结构体中参数的默认值既然,已经有了对应的默认值,那么我们就需要限制我们cli中的参数必须是这些内置参数中值 。
#[derive(Subcommand, Debug, Clone)]enum Commands {Create{#[arg(short = 'n',lnotallow="name",help = "用户信息",+value_parser = EnumValueParser::<Name>::new(),ignore_case = true)]+name: Option<Name>,#[arg(short = 'a',lnotallow="address",help = "地址信息",requires = "name",+value_parser = EnumValueParser::<Address>::new(),)]+address: Option<Address>,}}上面的配置,见名知意 , 就是从对应的枚举中解析对应的值 。
主函数其实,这步的操作和之前是差不多的,我们还是利用match对cli.command进行匹配处理 。不过我们这里又进一步的做了容错处理 。
  1. 首先判断是否提供子命令
  2. 在提供子命令的情况下,再判断是否是Craete
因为,在进行操作中我们会有错误抛出,所以我们对main的返回值也做了处理 。(anyhow::Result<()>)
fn main() ->anyhow::Result<()> {let cli = Cli::parse();match cli.command {// - 如果有子命令,则根据子命令执行相应的逻辑;Some(command) => {match command {Commands::Create {name,address,} =>operation_params(name,address)?,}},_ => panic!("Fatal: cli为提供参数,退出处理."),}Ok(())}operation_params在main中我们通过match是可以获取到cli中参数的,而此时我们还需要根据参数做进一步的处理 。我们把这个逻辑提取到了一个函数中了 。
fn operation_params (name: Option<Name>,address: Option<Address>) -> anyhow::Result<()> {let n = match name {Some(na) => na,None => {multiselect_msg("选择一个姓名:");message("使用上/下箭头进行选择,使用空格或回车键确认 。");let items = vec!["张三", "王五"];let selection = Select::with_theme(&ColorfulTheme::default()).items(&items).default(0).interact_on_opt(&Term::stderr())?;match selection {Some(0) => Name::N1,Some(1) => Name::N2,_ => panic!("Fatal: 用户信息制定错误."),}}};let a = match address {Some(na) => na,None => {multiselect_msg("选择一个地址:");message("使用上/下箭头进行选择,使用空格或回车键确认 。");let items = vec!["太原", "晋中"];let selection = Select::with_theme(&ColorfulTheme::default()).items(&items).default(0).interact_on_opt(&Term::stderr())?;match selection {Some(0) => Address::A1,Some(1) => Address::A2,_ => panic!("Fatal: 地址信息制定错误."),}}};println!("name:{:?},地址:{:?}",n,a);Ok(())}其实上面的逻辑也是比较简单明了的 。我们接收cli中的参数name/address 。因为他们都是枚举类型,所以我们继续用match进行对应值的匹配 。
虽然,我们对两个枚举值都做了处理,但是他们的逻辑都是相同的 。
上面的逻辑就是当我们运行子命令时候