53 次发布

0.9.12 2024年4月29日
0.9.11 2024年3月24日
0.9.9 2024年1月17日
0.9.8 2023年12月6日
0.3.2 2022年3月18日

#17 in 命令行界面

Download history 9776/week @ 2024-05-02 10701/week @ 2024-05-09 9811/week @ 2024-05-16 10304/week @ 2024-05-23 15504/week @ 2024-05-30 21012/week @ 2024-06-06 17165/week @ 2024-06-13 17119/week @ 2024-06-20 18441/week @ 2024-06-27 12079/week @ 2024-07-04 10202/week @ 2024-07-11 13625/week @ 2024-07-18 15316/week @ 2024-07-25 16230/week @ 2024-08-01 18365/week @ 2024-08-08 15959/week @ 2024-08-15

68,598 每月下载量
68 个crate中(42个直接使用) 使用

MIT/Apache

545KB
7.5K SLoC

bpaf

License: MIT OR Apache-2.0 bpaf on crates.io bpaf on docs.rs Source Code Repository bpaf on deps.rs

轻量级且灵活的命令行参数解析器,支持 derive 和组合式 API

derive 和组合式 API

bpaf 支持组合式和 derive API,并且可以同时混合使用这两种 API。这两个 API 提供了大部分相同的功能,有些事情用 derive 更方便(通常输入更少),有些用组合式(通常最大灵活性,减少样板结构)。在大多数情况下,使用其中一个就足够了。每当可能时,API 使用相同的键和整体结构。文档是共享的,并包含组合式和 derive 风格的示例。

bpaf 支持动态 shell 完成功能,适用于 bashzshfishelvish

快速入门 - 组合式和 derive API

derive 风格 API,点击展开
  1. 在您的 Cargo.toml 中将 bpaf 添加到 [dependencies]

    [dependencies]
    bpaf = { version = "0.9", features = ["derive"] }
    
  2. 定义一个包含命令行属性的结构,并运行生成的函数

    use bpaf::Bpaf;
    
    #[derive(Clone, Debug, Bpaf)]
    #[bpaf(options, version)]
    /// Accept speed and distance, print them
    struct SpeedAndDistance {
        /// Speed in KPH
        speed: f64,
        /// Distance in miles
        distance: f64,
    }
    
    fn main() {
        // #[derive(Bpaf)] generates `speed_and_distance` function
        let opts = speed_and_distance().run();
        println!("Options: {:?}", opts);
    }
    
  3. 尝试运行应用程序

    % very_basic --help
    Accept speed and distance, print them
    
    Usage: --speed=ARG --distance=ARG
    
    Available options:
            --speed=ARG     Speed in KPH
            --distance=ARG  Distance in miles
        -h, --help            Prints help information
        -V, --version         Prints version information
    
    % very_basic --speed 100
    Expected --distance ARG, pass --help for usage information
    
    % very_basic --speed 100 --distance 500
    Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
    
    % very_basic --version
    Version: 0.9.0 (taken from Cargo.toml by default)
    
组合式 API,点击展开
  1. 在您的 Cargo.toml 中将 bpaf 添加到 [dependencies]

    [dependencies]
    bpaf = "0.9"
    
    
    
  2. 声明组件解析器,将它们组合并运行

    use bpaf::{construct, long, Parser};
    #[derive(Clone, Debug)]
    struct SpeedAndDistance {
        /// Speed in KPH
        speed: f64,
        /// Distance in miles
        distance: f64,
    }
    
    fn main() {
        // primitive parsers
        let speed = long("speed")
            .help("Speed in KPG")
            .argument::<f64>("SPEED");
    
        let distance = long("distance")
            .help("Distance in miles")
            .argument::<f64>("DIST");
    
        // parser containing information about both speed and distance
        let parser = construct!(SpeedAndDistance { speed, distance });
    
        // option parser with metainformation attached
        let speed_and_distance
            = parser
            .to_options()
            .descr("Accept speed and distance, print them");
    
        let opts = speed_and_distance.run();
        println!("Options: {:?}", opts);
    }
    
  3. 尝试运行应用程序

    % very_basic --help
    Accept speed and distance, print them
    
    Usage: --speed=ARG --distance=ARG
    
    Available options:
            --speed=ARG     Speed in KPH
            --distance=ARG  Distance in miles
        -h, --help          Prints help information
        -V, --version       Prints version information
    
    % very_basic --speed 100
    Expected --distance ARG, pass --help for usage information
    
    % very_basic --speed 100 --distance 500
    Options: SpeedAndDistance { speed: 100.0, distance: 500.0 }
    
    % very_basic --version
    Version: 0.5.0 (taken from Cargo.toml by default)
    

设计目标:灵活性、可重用性、正确性

库允许通过构建单个参数的解析器并使用一个宏将那些原始解析器组合起来来消费命令行参数。例如,可以将需要一个浮点数的解析器转换为需要多个浮点数或可选的解析器,这样不同的子命令或二进制文件就可以共享大量代码

// a regular function that doesn't depend on any context, you can export it
// and share across subcommands and binaries
fn speed() -> impl Parser<f64> {
    long("speed")
        .help("Speed in KPH")
        .argument::<f64>("SPEED")
}

// this parser accepts multiple `--speed` flags from a command line when used,
// collecting results into a vector
fn multiple_args() -> impl Parser<Vec<f64>> {
    speed().many()
}

// this parser checks if `--speed` is present and uses value of 42.0 if it's not
fn with_fallback() -> impl Parser<f64> {
    speed().fallback(42.0)
}

在任何时候,都可以根据当前每个子解析器的解析状态应用额外的验证或回退值,并且可以有多个阶段

#[derive(Clone, Debug)]
struct Speed(f64);
fn speed() -> impl Parser<Speed> {
    long("speed")
        .help("Speed in KPH")
        .argument::<f64>("SPEED")

        // You can perform additional validation with `parse` and `guard` functions
        // in as many steps as required.
        // Before and after next two applications the type is still `impl Parser<f64>`
        .guard(|&speed| speed >= 0.0, "You need to buy a DLC to move backwards")
        .guard(|&speed| speed <= 100.0, "You need to buy a DLC to break the speed limits")

        // You can transform contained values, next line gives `impl Parser<Speed>` as a result
        .map(|speed| Speed(speed))
}

该库在可能的情况下遵循“解析,不验证”的方法。通常您只需解析一次值,然后以Rust结构体/枚举的形式获取结果,这两种API都使用严格的数据类型。

设计目标:限制

该库设定的主要限制是您不能使用解析值(但不是解析成功或失败的事实)来决定如何解析后续值。换句话说,解析器不具有单子强度,只有应用强度。

以下是一个示例

程序接受--stdout--file标志之一来指定输出目标,当是--file时,程序还要求具有文件名的-f属性

但不能是这样

程序接受带有可能值-o'stdout''file'-o属性,当是'file'时,程序还要求具有文件名的-f属性

这组限制使得bpaf能够提取关于计算结构的信息,以生成帮助、动态补全和整体结果,从而在用户体验上更清晰

bpaf不执行参数名称验证,实际上具有多个相同名称的参数是可以的,您可以将其作为替代方案组合,并且不执行除fallback之外的重试。您需要注意宏内替代方案的顺序:消耗命令行上最左边可用参数的解析器获胜,如果这相同 - 最左边的解析器获胜。因此,为了解析既可以是switch也可以是argument的参数--test,您应该首先放置参数。

您必须在结构体的末尾放置positional项,或者在derive API中将它们作为最后一个参数消耗。

动态shell补全

bpaf实现了shell补全,允许自动填充标志和命令名称,以及参数和位置项值。

  1. 启用autocomplete功能

    bpaf = { version = "0.9", features = ["autocomplete"] }
    
  2. 使用complete装饰argumentpositional解析器以自动完成参数值

  3. 根据您的shell,它生成适当的补全文件并将其放置在shell将要查找它的位置。文件名应与程序名称以某种方式相对应。请咨询您的shell手册以获取位置和命名约定

    1. bash

      $ your_program --bpaf-complete-style-bash >> ~/.bash_completion
      
    2. zsh:注意文件名开头的_

      $ your_program --bpaf-complete-style-zsh > ~/.zsh/_your_program
      
    3. fish

      $ your_program --bpaf-complete-style-fish > ~/.config/fish/completions/your_program.fish
      
    4. elvish

      $ your_program --bpaf-complete-style-elvish >> ~/.config/elvish/rc.elv
      
  4. 重新启动您的shell - 您只需这样做一次,或者在bpaf主要版本升级后可选:生成的补全文件仅包含如何请求程序可能的补全的指令,并且即使选项不同也不会更改。

  5. 生成的脚本依赖于您的程序可以通过 $PATH 访问。

更多示例

您可以在以下位置找到更多示例: https://github.com/pacak/bpaf/tree/master/examples

它们通常都有文档或至少包含对重要部分的解释,您可以通过克隆仓库并运行来查看它们的工作方式。

$ cargo run --example example_name

测试您自己的解析器

您可以使用 run_inner 测试您自己的解析器以保持兼容性,或者简单地检查预期输出。

#[derive(Debug, Clone, Bpaf)]
#[bpaf(options)]
pub struct Options {
    pub user: String
}

#[test]
fn test_my_options() {
    let help = options()
        .run_inner(&["--help"])
        .unwrap_err()
        .unwrap_stdout();
    let expected_help = "\
Usage --user=ARG
<skip>
";

    assert_eq!(help, expected_help);
}

Cargo 功能

  • derive:添加对 bpaf_derive 仓库的依赖,并重新导出 Bpaf derive 宏。您需要启用它才能使用 derive API。默认禁用。

  • extradocs:用于内部将教程包含到 https://docs.rs/bpaf 中,除非您想构建自己的文档副本(https://github.com/rust-lang/cargo/issues/8905),否则无需在本地开发中启用它。默认禁用。

  • batteries:使用公共 bpaf API 实现的帮助程序。默认禁用。

  • autocomplete:启用对 shell 自动补全的支持。默认禁用。

  • bright-colordull-color:在打印 --help 和类似内容时使用更多颜色。启用任何颜色功能都会添加一些额外的依赖项,并可能提高 MRSV。如果您计划在发布的应用程序中使用此功能,则最好将其作为功能标志公开。

    [features]
    bright-color = ["bpaf/bright-color"]
    dull-color = ["bpaf/dull-color"]
    

    默认禁用。

  • docgen:从帮助声明生成文档,请参阅 OptionParser::render_markdown。默认禁用。

依赖关系