86·实战项目进阶

实战:CLI 工具

实战:CLI 工具

学习目标

  1. 综合运用 clap、serde、anyhow
  2. 实现文件处理 CLI
  3. 掌握错误处理最佳实践

实战项目:单词计数工具

Cargo.toml

[package]
name = "wordcount"
version = "0.1.0"
edition = "2021"

[dependencies]
clap = { version = "4", features = ["derive"] }
anyhow = "1"

src/main.rs

use clap::Parser;
use anyhow::{Result, Context};
use std::path::PathBuf;
use std::fs;

#[derive(Parser)]
#[command(name = "wordcount")]
#[command(about = "统计文件中的单词数、行数、字符数")]
struct Args {
    /// 要统计的文件
    files: Vec<PathBuf>,

    /// 显示行数
    #[arg(short = 'l', long)]
    lines: bool,

    /// 显示单词数
    #[arg(short = 'w', long)]
    words: bool,

    /// 显示字符数
    #[arg(short = 'c', long)]
    chars: bool,
}

#[derive(Default)]
struct Stats {
    lines: usize,
    words: usize,
    chars: usize,
}

fn count(content: &str) -> Stats {
    Stats {
        lines: content.lines().count(),
        words: content.split_whitespace().count(),
        chars: content.chars().count(),
    }
}

fn main() -> Result<()> {
    let args = Args::parse();

    let show_all = !args.lines && !args.words && !args.chars;

    for path in &args.files {
        let content = fs::read_to_string(path)
            .context(format!("无法读取文件: {}", path.display()))?;

        let stats = count(&content);

        let mut parts = vec![];
        if show_all || args.lines {
            parts.push(format!("{}", stats.lines));
        }
        if show_all || args.words {
            parts.push(format!("{}", stats.words));
        }
        if show_all || args.chars {
            parts.push(format!("{}", stats.chars));
        }

        println!("{} {}", parts.join("\t"), path.display());
    }

    Ok(())
}

练习扩展

  1. 添加 --sort 选项按行数排序
  2. 支持从 stdin 读取
  3. 添加颜色输出

小结

技能应用
clap命令行解析
anyhow错误处理
标准库文件读写
迭代器文本处理

练习编辑器

rust
Loading...