用rust编写你自己的命令行程序 - 1 界面显示

512次阅读  |  发布于11月以前

这是一篇关于如何在rust中构建自己的命令行程序的文章。下图是CLI的界面:

我们将要建造什么?

我们要构建的CLI其实很简单,但是它很有用。我们将构建一个实现“key-value”存储的终端界面。有5个基本命令:

经典的CRUDS应用,这就是我们要做的。

创建项目

首先,执行如下命令创建一个Rust新项目:

cargo new mini-kv

然后,在Cargo.toml文件中加入如下依赖项:

[dependencies]
clap = {version = "4.3.23", features = ["derive"]}
sqlx = { version = "0.7", features = [ "runtime-tokio", "sqlite" ] }
serde = {version = "*", features=["derive"]}
bunt = "*"
inquire = "*"
tokio = {version="*", features=["full"]}
chrono = "*"
rand = "*"
dotenv = {version="*"}
tabled ={ version="*"}
human-panic = "*"

我们将使用相当多的依赖库来创建一个漂亮的CLI:

再然后,在项目中创建如下所示的所有文件:

开始编码

在src/main.rs文件中,写入如下代码:

mod db;
mod utils;
mod handler;

use clap::Parser;
use dotenv::dotenv;
use human_panic::setup_panic;

// Args结构体用于解析命令行参数
#[derive(Parser, Debug)]
#[command(name="MiniKey", author="", version, about="一个简单的键值存储", long_about = None)]
struct Args {
    #[arg(required=false)]
    cmd: Option<String>,

    #[arg(short, long)]
    custom: Option<String>,

    #[arg(short, long)]
    docs: bool,
}

cmd是一个可选参数,如果没有输入,我们将提示用户。

main函数的代码如下:

#[tokio::main]
async fn main() {
    // 加载.env文件
    dotenv().ok();

    //human-panic 初始化
    setup_panic!();

    // 打印 logo 
    let logo = r#"
    ___  ____       _   _              
    |  \/  (_)     (_) | |             
    | .  . |_ _ __  _  | | _____ _   _ 
    | |\/| | | '_ \| | | |/ / _ \ | | |
    | |  | | | | | | | |   <  __/ |_| |
    \_|  |_/_|_| |_|_| |_|\_\___|\__, |
                                __/ |
                                |___/ 
    "#;
    bunt::println!("{$green}{}{/$}", logo);

    let args = Args::parse();

    let cmd: String;
    if args.cmd.is_some(){
        cmd = args.cmd.unwrap();
    } else{
        cmd = inquire::Text::new("输入命令: ").with_help_message("输入一个有效命令").with_autocomplete(
            &utils::suggester,
        ).prompt().unwrap();
    }

    println!("用户输入的命令是:{}", cmd);
}

src/utils.rs文件中的suggester函数用于过滤用户输入的命令,如果用户没有输入命令,则显示全部命令供用户选择。代码如下:

pub fn suggester(val: &str) -> Result<Vec<String>, Box<dyn std::error::Error + Send + Sync>> {
    let suggestions = [
        "get",
        "set",
        "delete",
        "list",
        "search",
        "help",
        "exit",
    ];

    let val_lower = val.to_lowercase();

    Ok(suggestions
        .iter()
        .filter(|s| s.to_lowercase().contains(&val_lower))
        .map(|s| String::from(*s))
        .collect())
}

执行cargo run,则会显示出文章开头的命令行界面。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8