#arguments-parser #clap #quick #cli #macro

macro_clap

命令行参数解析简化方案

1 个不稳定版本

0.1.0 2022年11月28日

#62 in #quick

MIT/Apache

34KB
636 代码行数(不含注释)

macro_clap

一种快速便捷的方式来实现命令行参数解析,具有自动生成的彩色使用说明,以及支持选项和参数分支的功能

示例

use macro_clap::*;

const INTRODUCTION: &'static str = "Whatever you want your intro to be";

cli!(
    const ARG_PARSER: ArgParser<INTRODUCTION> = [
        branch!(action as ActionEnum {
            "copy" |> Copy => {
                arg!(source as String),
                arg!(dest as String)
            },
            "delete" |> Delete => {
                arg!(source as String)
            },
            "modify" |> Modify => {
                branch!(modif_type as ModifType {
                    "replace" |> Replace => {},
                    "append" |> Append => {}
                }),
                arg!(source as String),
                arg!(text as String),
                maybe!(encoding as (Option<String>)),
                opt!(options as ModifyOptions {
                    interleave: ["--interleave"] -> (GrabLast<char>)
                })
            }
        }),
        opt!(options as OptionStruct {
            verbose: ["-v", "--verbose"] -> (Counter<u8>),
            explain: ["-x", "--explain"] -> Flag
        }),
        collect!(rest as (Vec<String>))
    ]
);

fn main() {
    match ARG_PARSER.parse_args() {
        Ok(args) => {
            println!("{:?}", args);
        },
        Err(e) => {
            println!("{e}");
            return;
        }
    };
}

使用说明

如果 Err(String) 的情况在控制台中打印,你的代码将具有以下行为

  • 在命令行中调用 your_crate 将在控制台中打印出介绍信息以及使用说明
  • 调用 your_crate --help 将只打印出使用说明
  • 调用 your_crate (一些错误的参数配置) 将打印出错误信息和使用说明
  • 如果提供了过多的参数,并且没有设置任何 collect! 宏,它将打印出错误信息和使用说明

限制

在 macro_clap 中,所有以 '-' 开头的参数都被视为选项,其余的被视为普通参数。你对于错误信息的控制非常有限,它们可能有时对经验不足的用户不太有用。这是因为 macro_clap 优先考虑代码的简洁性而不是可用性,适合那些想快速编写功能 CLI 而不需要过多考虑实现细节的人,例如制作原型版本。

语法

// cli! is the root of your command line interface
// SomeUniqueType is just a type that the macro needs to make the cli
// You CAN make multiple cli! in the same file or same crate, as long as their types are not shared
// YOUR_INTRODUCTION must be either a &'static str or a const &'static str
// Once your cli! macro is constructed, use YOUR_CONST_NAME.parse_args() to parse the arguments
// The return value of YOUR_CONST_NAME.parse_args() is Result<(Arguments, String)>
//   where Arguments is the tuple of the result of the parsing of all arguments
cli!(
    const YOUR_CONST_NAME: SomeUniqueType<YOUR_INTRODUCTION> = [
        /* list of all the arguments */
    ]
)

// This tells the macro to wait for an argument and to parse it as ArgType
// If no argument is passed, the macro will fail
// ArgType must be String, bool, or an integer
arg!(arg_name as ArgType)

// This tells the macro to wait for an argument and to parse it as Some(ArgType)
// However, unlike arg!, maybe! will not fail if no argument is given, but return None instead
// ArgType must be String, bool, or an integer
// Please do not forget to wrap ArgType in an Option and to surrond everything by parentheses
maybe!(maybe_arg_name as (Option<ArgType>))

// This tells the macro to wait for a keyword in the list
// If no keyword is passed, or if the keyword is not present in the list, the macro will fail
// BranchEnum is the type that will be returned by the branch! macro
// BranchEnum is automatically implemented by the macro, so make sure that it is unique
// Variants of BranchEnum are tuple variants, and contain the results of the arguments specific to their own path
branch!(branch_name as BranchEnum {
    "keyword_1" |> Variant1 => {
        /* list of args if keyword_1 */
    },
    "keyword_2" |> Variant2 => {
        /* list of args if keyword_2 */
    },
    // You can make as many keywords as you want
}),

// This tells the macro to start parsing as many options as possible
// When the macro stops finding options, it just continues
// Lists of keywords related to options can be for example ["-v", "--verbose"]
// OptionTypes dictates how the options are handled
// OptionTypes are Counter, Flag, FlagCounter, GrabFirst, GrabLast and GrabAll
// Please see their documentation for more information about how they operate
// Please make sure that every OptionType is surrounded by parenthesis if it is a generic type
//  i.e. (Counter<i8>) or (GrabFirst<String>)
opt!(option_group_name as OptionStruct {
    option_1_name: [ /* list of all the keywords related to option 1 */ ] -> OptionType1,
    option_2_name: [ /* list of all the keywords related to option 2 */ ] -> OptionType2,
    // You can make as many options as you want
}),

// This will dump every argument left over by the previous parsing into a Vec<String>
// Please note that the return type of collect! MUST be (Vec<String>)
// Please make sure that nothing follows a collect!, as it will never recieve any arguments
collect!(rest as (Vec<String>))

无运行时依赖