Rust命令行工具生成库make-cmd的使用,快速构建高效可扩展的CLI应用程序

Rust命令行工具生成库make-cmd的使用,快速构建高效可扩展的CLI应用程序

安装

在项目目录中运行以下Cargo命令:

cargo add make-cmd

或者在Cargo.toml中添加以下行:

make-cmd = "0.1.0"

使用示例

以下是一个完整的示例demo,展示如何使用make-cmd库构建CLI应用程序:

use make_cmd::cmd;

// 定义你的CLI命令结构
#[derive(Debug, cmd::Command)]
struct MyCli {
    /// 输入文件路径
    #[cmd(arg)]
    input: String,
    
    /// 输出文件路径 (可选)
    #[cmd(arg, optional)]
    output: Option<String>,
    
    /// 详细输出
    #[cmd(flag, short = 'v')]
    verbose: bool,
    
    /// 线程数 (默认为1)
    #[cmd(option, short = 't', default = "1")]
    threads: usize,
}

fn main() {
    // 解析命令行参数
    let cli = MyCli::parse();
    
    println!("解析得到的CLI参数: {:?}", cli);
    
    // 在这里添加你的应用程序逻辑
    if cli.verbose {
        println!("详细模式已启用");
    }
    
    println!("使用 {} 个线程处理文件", cli.threads);
}

功能说明

  1. 参数解析

    • #[cmd(arg)] - 必需的位置参数
    • #[cmd(arg, optional)] - 可选的位置参数
    • #[cmd(flag)] - 布尔标志
    • #[cmd(option)] - 带值的选项
  2. 自动生成帮助信息

    • 结构体字段的文档注释会自动成为CLI帮助信息的一部分
    • 支持-h--help显示帮助
  3. 自定义选项

    • 使用short属性指定短选项名
    • 使用default属性指定默认值

构建和运行

构建并运行你的CLI应用程序:

cargo build
./target/debug/your_binary --help  # 查看帮助信息
./target/debug/your_binary input.txt -v -t 4  # 运行示例

这个示例展示了如何使用make-cmd快速构建一个功能完整的CLI应用程序,包括参数解析、帮助生成和默认值处理等功能。

完整示例代码

下面是一个更完整的示例,展示了make-cmd库的高级用法:

use make_cmd::cmd;
use std::path::PathBuf;

/// 一个更复杂的CLI应用程序示例
#[derive(Debug, cmd::Command)]
struct AdvancedCli {
    /// 操作模式 (create|delete|list)
    #[cmd(arg)]
    mode: String,
    
    /// 目标文件路径
    #[cmd(arg)]
    file: PathBuf,
    
    /// 递归操作
    #[cmd(flag, short = 'r')]
    recursive: bool,
    
    /// 操作确认
    #[cmd(flag, short = 'y')]
    confirm: bool,
    
    /// 日志级别 (debug|info|warn|error)
    #[cmd(option, short = 'l', default = "info")]
    log_level: String,
    
    /// 配置文件路径
    #[cmd(option, short = 'c')]
    config: Option<PathBuf>,
    
    /// 额外参数 (可多个)
    #[cmd(arg, optional, multiple)]
    extra_args: Vec<String>,
}

fn main() {
    let cli = AdvancedCli::parse();
    
    match cli.mode.as_str() {
        "create" => {
            println!("创建文件: {:?}", cli.file);
            if cli.recursive {
                println!("递归模式已启用");
            }
        },
        "delete" => {
            if !cli.confirm {
                eprintln!("请使用 -y 确认删除操作");
                return;
            }
            println!("删除文件: {:?}", cli.file);
        },
        "list" => {
            println!("列出文件: {:?}", cli.file);
        },
        _ => {
            eprintln!("未知模式: {}", cli.mode);
            return;
        }
    }
    
    println!("日志级别: {}", cli.log_level);
    
    if let Some(config_path) = cli.config {
        println!("使用配置文件: {:?}", config_path);
    }
    
    if !cli.extra_args.is_empty() {
        println!("额外参数: {:?}", cli.extra_args);
    }
}

这个完整示例展示了:

  • 枚举式参数(mode)
  • 路径参数(PathBuf)
  • 多个布尔标志
  • 默认值设置
  • 可选配置参数
  • 多个额外参数的处理
  • 基本的错误处理逻辑

可以通过以下命令测试:

# 查看帮助
cargo run -- --help

# 创建文件示例
cargo run -- create test.txt -r -l debug -c config.toml arg1 arg2

# 删除文件示例
cargo run -- delete test.txt -y

1 回复

Rust命令行工具生成库make-cmd使用指南

简介

make-cmd是一个Rust库,用于快速构建高效且可扩展的命令行接口(CLI)应用程序。它简化了命令行参数解析、子命令管理和帮助文档生成的过程,让开发者能专注于业务逻辑的实现。

主要特性

  • 简洁的API设计
  • 自动生成帮助文档
  • 支持嵌套子命令
  • 类型安全的参数解析
  • 可扩展的命令结构

安装

Cargo.toml中添加依赖:

[dependencies]
make-cmd = "0.1"

基本使用方法

1. 创建简单命令

use make_cmd::Command;

fn main() {
    let cmd = Command::new("greet")
        .about("A simple greeting application")
        .version("1.0.0")
        .arg("name", "The person to greet")
        .action(|args| {
            let name = args.get_one::<String>("name").unwrap();
            println!("Hello, {}!", name);
            Ok(())
        });
    
    cmd.run();
}

运行示例:

$ greet Rust
Hello, Rust!

2. 创建带子命令的应用

use make_cmd::{Command, SubCommand};

fn main() {
    let cmd = Command::new("file-manager")
        .about("A simple file manager")
        .version("1.0.0")
        .subcommand(
            SubCommand::new("create")
                .about("Create a new file")
                .arg("filename", "Name of the file to create")
                .action(|args| {
                    let filename = args.get_one::<String>("filename").unwrap();
                    std::fs::File::create(filename)?;
                    println!("Created file: {}", filename);
                    Ok(())
                })
        )
        .subcommand(
            SubCommand::new("delete")
                .about("Delete a file")
                .arg("filename", "Name of the file to delete")
                .action(|args| {
                    let filename = args.get_one::<String>("filename").unwrap();
                    std::fs::remove_file(filename)?;
                    println!("Deleted file: {}", filename);
                    Ok(())
                })
        );
    
    cmd.run();
}

运行示例:

$ file-manager create test.txt
Created file: test.txt

$ file-manager delete test.txt
Deleted file: test.txt

3. 添加可选参数和标志

use make_cmd::Command;

fn main() {
    let cmd = Command::new("calculator")
        .about("A simple calculator")
        .version("1.0.0")
        .arg("x", "First number")
        .arg("y", "Second number")
        .flag("add", "Add the numbers")
        .flag("multiply", "Multiply the numbers")
        .action(|args| {
            let x: f64 = args.get_one("x").unwrap();
            let y: f64 = args.get_one("y").unwrap();
            
            if args.is_present("add") {
                println!("Result: {}", x + y);
            } else if args.is_present("multiply") {
                println!("Result: {}", x * y);
            } else {
                println!("Please specify an operation (--add or --multiply)");
            }
            
            Ok(())
        });
    
    cmd.run();
}

运行示例:

$ calculator 5 3 --add
Result: 8

$ calculator 5 3 --multiply
Result: 15

高级用法

1. 自定义参数解析器

use make_cmd::{Command, Arg};
use std::str::FromStr;

struct Point {
    x: i32,
    y: i32,
}

impl FromStr for Point {
    type Err = String;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let parts: Vec<&str> = s.split(',').collect();
        if parts.len() != 2 {
            return Err("Point must be in format 'x,y'".to_string());
        }
        Ok(Point {
            x: parts[0].parse().map_err(|_| "Invalid x coordinate".to_string())?,
            y: parts[1].parse().map_err(|_| "Invalid y coordinate".to_string())?,
        })
    }
}

fn main() {
    let cmd = Command::new("point-ops")
        .about("Point operations")
        .arg(Arg::new("point")
            .help("Point in format 'x,y'")
            .required(true)
            .parser::<Point>())
        .action(|args| {
            let point = args.get_one::<Point>("point").unwrap();
            println!("Point coordinates: ({}, {})", point.x, point.y);
            Ok(())
        });
    
    cmd.run();
}

运行示例:

$ point-ops 10,20
Point coordinates: (10, 20)

2. 异步命令处理

use make_cmd::Command;
use tokio::fs;

#[tokio::main]
async fn main() {
    let cmd = Command::new("async-file")
        .about("Async file operations")
        .arg("filename", "File to read")
        .async_action(|args| async move {
            let filename = args.get_one::<String>("filename").unwrap();
            let contents = fs::read_to_string(filename).await?;
            println!("File contents:\n{}", contents);
            Ok(())
        });
    
    cmd.run_async().await;
}

最佳实践

  1. 模块化设计:将不同子命令的实现放在不同的模块中
  2. 错误处理:使用anyhowthiserror库进行友好的错误处理
  3. 测试:为每个命令和子命令编写单元测试
  4. 文档:为每个命令和参数添加详细的帮助文本
  5. 性能:对于CPU密集型操作,考虑使用rayon进行并行处理

完整示例

下面是一个完整的文件管理工具示例,结合了基本和高级用法:

use make_cmd::{Command, SubCommand, Arg};
use std::path::PathBuf;
use anyhow::Result;

// 定义自定义文件大小类型
#[derive(Debug)]
struct FileSize(u64);

impl std::str::FromStr for FileSize {
    type Err = String;
    
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let size = s.parse().map_err(|_| "Invalid file size".to_string())?;
        Ok(FileSize(size))
    }
}

fn main() -> Result<()> {
    let cmd = Command::new("file-tool")
        .about("Advanced file management tool")
        .version("1.0.0")
        .subcommand(
            SubCommand::new("info")
                .about("Get file information")
                .arg(Arg::new("path")
                    .help("File path")
                    .required(true))
                .arg(Arg::new("human-readable")
                    .help("Show size in human readable format")
                    .short('h')
                    .long("human-readable")
                    .takes_value(false))
                .action(|args| {
                    let path = args.get_one::<String>("path").unwrap();
                    let metadata = std::fs::metadata(path)?;
                    
                    if args.is_present("human-readable") {
                        println!("Size: {} KB", metadata.len() / 1024);
                    } else {
                        println!("Size: {} bytes", metadata.len());
                    }
                    
                    Ok(())
                })
        )
        .subcommand(
            SubCommand::new("search")
                .about("Search files")
                .arg(Arg::new("directory")
                    .help("Directory to search")
                    .required(true))
                .arg(Arg::new("extension")
                    .help("File extension filter")
                    .short('e')
                    .long("extension"))
                .arg(Arg::new("min-size")
                    .help("Minimum file size in bytes")
                    .long("min-size")
                    .parser::<FileSize>())
                .async_action(|args| async move {
                    let dir = args.get_one::<String>("directory").unwrap();
                    let extension = args.get_one::<String>("extension");
                    let min_size = args.get_one::<FileSize>("min-size");
                    
                    // 异步文件搜索逻辑
                    search_files(dir, extension, min_size).await?;
                    
                    Ok(())
                })
        );
    
    cmd.run()?;
    Ok(())
}

async fn search_files(dir: &str, extension: Option<&String>, min_size: Option<&FileSize>) -> Result<()> {
    // 实现异步文件搜索逻辑
    println!("Searching in: {}", dir);
    println!("Filters - Extension: {:?}, Min Size: {:?}", extension, min_size);
    Ok(())
}

总结

make-cmd为Rust开发者提供了一个简单而强大的工具来构建CLI应用程序。通过其直观的API和丰富的功能,你可以快速创建出专业级的命令行工具,而无需花费大量时间处理参数解析和帮助文档生成等底层细节。

回到顶部