Clap 简单使用
Contents
添加参数
Basic
以 myapp run app --release --message hello
为例
这里的参数分为几种
run
子命令app
固定位置参数--release
flag 参数--message hello
命名选项参数
子命令
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands
}
#[derive(Subcommand)]
enum Commands {
Add {
name: Option<String>
}
}
fn main() {
let cli = Cli::parse();
match &cli.command {
Commands::Add {name} => {
println!("myapp add was used, name is: {:?}", name)
}
}
}
- 在结构中使用
#[command(subcommand)]
表示以下结构是一个子命令 - 用
#[derive(Subcommand)]
来声明一个子命令结构 - 调用的时候,使用
myapp add hello
即可
固定位置参数
use clap::Parser;
#[derive(Debug, Parser)]
struct Cli {
a: u32,
b: u32,
c: u32,
name: String
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
程序的解析顺序好像是按照结构体中字段的声明顺序决定的 这个程序应该这样调用
myapp 1 2 3 hello
命名选项参数
使用这个的时候,需要在字段上添加一个 #[arg(short, long)]
,就可以生成短选项和长选项
use clap::Parser;
#[derive(Debug, Parser)]
struct Cli {
a: u32,
b: u32,
c: u32,
#[arg(long)]
name: String
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
这样调用程序
myapp 1 2 3 --name hello
Flag 参数
use clap::Parser;
#[derive(Debug, Parser)]
struct Cli {
a: u32,
b: u32,
c: u32,
#[arg(long)]
name: String,
#[arg(long)]
verbose: bool
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
这个很简单,先添加 #[arg(long)]
或者 #[arg(short)]
宏,再将字段类型改为 bool
即可
这样调用程序
myapp 1 2 3 --name hello --verbose
可选的参数
这个也很简单,将字段类型改为 Option
即可,这样程序找不到匹配的项时,会将值设置为 None
use clap::Parser;
#[derive(Debug, Parser)]
struct Cli {
a: u32,
b: u32,
c: Option<u32>,
#[arg(long)]
name: String,
#[arg(long)]
verbose: bool,
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}
这样调用程序
myapp 1 2 --name hello --verbose
Advanced
更进一步的,我们可以对参数进行验证和添加默认值
默认值
根据文档上的描述,只需要在字段上加上 #[arg(default_value_t = ?)]
use clap::Parser;
#[derive(Parser)]
struct Cli {
#[arg(default_value_t = 2020)]
port: u16
}
fn main() {
let cli = Cli::parse();
println!("port: {:?}", cli.port);
}
验证
我们手动制定一个验证器函数,这个函数要返回一个 Result
类型,接受一个 &str
参数
use std::ops::RangeInclusive;
const PORT_RANGE: RangeInclusive<usize> = 1..=65535;
fn port_in_range(s: &str) -> Result<u16, String> {
let port: usize = s
.parse()
.map_err(|_| format!("`{}` isn't a port number", s))?;
if PORT_RANGE.contains(&port) {
Ok(port as u16)
} else {
Err(format!(
"Port not in range {} - {}",
PORT_RANGE.start(),
PORT_RANGE.end()
))
}
}
Result
的左值类型要与字段类型匹配
#[derive(Debug)]
struct Cli {
#[arg(value_parser = port_in_range)]
port: u16
}
fn main() {
let cli = Cli::parse();
println!("port = {}", cli.port);
}
想必你也发现了,这个验证器不仅是个验证器,他也是个转换器,我们自己做个转换器,把输入数字的负数打印出来
use clap::Parser;
#[derive(Debug, Parser)]
struct Cli {
a: u32,
b: u32,
#[arg(value_parser = convert)]
c: i32,
#[arg(long)]
name: String,
#[arg(long)]
verbose: bool,
}
fn convert(s: &str) -> Result<i32, String> {
let number: i32 = s
.parse()
.map_err(|_| format!("`{}` isn't a valid number", s))?;
return -number as i32;
}
fn main() {
let cli = Cli::parse();
println!("{:?}", cli);
}