实战:CLI 工具
学习目标
- 综合运用 clap、serde、anyhow
- 实现文件处理 CLI
- 掌握错误处理最佳实践
实战项目:单词计数工具
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(())
}
练习扩展
- 添加
--sort选项按行数排序 - 支持从 stdin 读取
- 添加颜色输出
小结
| 技能 | 应用 |
|---|---|
| clap | 命令行解析 |
| anyhow | 错误处理 |
| 标准库 | 文件读写 |
| 迭代器 | 文本处理 |
练习编辑器
rust
Loading...