当前位置: 首页 > news >正文

Rust 命令行待办工具

Rust项目实战- 命令行待办工具全解析

一、项目概述

1.1 项目背景

Rust 兼具内存安全与高性能。本项目借助 Rust 开发命令行待办工具,既利用 Rust 的性能优势,又通过合理设计满足日常待办管理需求,后续还新增优先级管理与关键词搜索功能,进一步提升工具实用性。

1.2 核心功能

  • 基础功能:添加、查看、标记完成、删除待办,数据以 JSON 格式存储在本地 todo.json 文件,实现持久化,重启工具数据不丢失。

  • 新增功能

    • 优先级管理:支持 Low/Medium/High 三种优先级,添加待办可指定优先级,查看待办按优先级降序排序。

    • 关键词搜索:新增搜索命令,按关键词筛选待办内容,搜索不区分大小写,结果按优先级排序显示。

1.3 技术栈

  • 基础语法:变量、函数、结构体、枚举、循环、条件判断。

  • 第三方库:

    • clap:解析命令行参数,支持子命令与可选参数配置。

    • serde + serde_json:实现 JSON 数据的序列化与反序列化,用于待办数据的读写。

    • chrono:处理时间,记录待办创建时间,支持时间格式转换。

  • 标准库:文件 IO(std::fs)用于读写本地文件,std::path 处理文件路径,错误处理(Result 类型)处理各类异常情况。

二、环境准备

2.1 安装依赖工具

工具版本要求安装方法验证命令
Rust 工具链1.70+官网下载 rustup-init,默认安装rustc --version

在这里插入图片描述

三、项目初始化与配置

3.1 创建项目

打开终端,执行以下命令创建 Rust 项目:

cargo new rust_todo_cli
cd rust_todo_cli

生成的初始目录结构如下:

rust_todo_cli/
├─ src/
│  └─ main.rs    # 主程序文件,处理命令行交互逻辑
└─ Cargo.toml    # 项目配置文件,管理依赖与项目信息

3.2 配置依赖

修改 Cargo.toml 文件,在 [dependencies] 下添加所需第三方库依赖:

[package]
name = "rust_todo_cli"
version = "0.1.0"
edition = "2021"[dependencies]
# 命令行参数解析(支持子命令与参数配置)
clap = { version = "4.0", features = ["derive"] }
# JSON 序列化/反序列化(处理待办数据存储)
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
# 时间处理(记录待办创建时间)
chrono = { version = "0.4", features = ["serde", "std"] }

3.3 模块导出配置

新建 src/lib.rs 文件,用于导出自定义模块,方便 main.rs 调用:

// src/lib.rs
pub mod todo; // 导出 todo 模块,包含待办数据结构与核心操作

四、核心代码实现

4.1 待办数据结构与核心操作(src/todo.rs)

use chrono::{DateTime, Local, Utc};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs;
use std::path::Path;
use std::str::FromStr;// 待办优先级枚举(支持排序、序列化、命令行参数解析)
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Priority {Low,    // 低优先级Medium, // 中优先级(默认)High,   // 高优先级
}// 待办任务状态:未完成/已完成
#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
pub enum TodoStatus {Pending,   // 未完成Completed, // 已完成
}// 待办任务结构体(包含优先级字段)
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Todo {pub id: usize,                // 唯一序号(用于标记/删除)pub content: String,          // 任务内容pub created_at: DateTime<Utc>,// 创建时间(UTC 时间,避免时区问题)pub status: TodoStatus,       // 任务状态pub priority: Priority,       // 任务优先级
}// 待办列表(封装待办的核心操作)
#[derive(Debug, Serialize, Deserialize)]
pub struct TodoList {pub todos: Vec<Todo>, // 存储所有待办
}// 优先级相关方法(图标转换、文字描述、默认值、命令行解析)
impl Priority {// 优先级转图标(用于友好显示)pub fn to_icon(&self) -> &str {match self {Priority::Low => "🔵",Priority::Medium => "🟡",Priority::High => "🔴",}}// 优先级转文字描述(用于终端输出)pub fn to_str(&self) -> &str {match self {Priority::Low => "低",Priority::Medium => "中",Priority::High => "高",}}
}// 实现 Display 特性(枚举转字符串,支持 clap 命令行参数显示)
impl fmt::Display for Priority {fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {match self {Priority::Low => write!(f, "Low"),Priority::Medium => write!(f, "Medium"),Priority::High => write!(f, "High"),}}
}// 实现 FromStr 特性(字符串转枚举,支持 clap 解析用户输入)
impl FromStr for Priority {type Err = String;fn from_str(s: &str) -> Result<Self, Self::Err> {match s.to_lowercase().as_str() {"low" => Ok(Priority::Low),"medium" => Ok(Priority::Medium),"high" => Ok(Priority::High),_ => Err(format!("无效的优先级:{}!仅支持 Low/Medium/High(不区分大小写)",s)),}}
}// 为优先级实现默认值(默认中优先级)
impl Default for Priority {fn default() -> Self {Priority::Medium}
}// 待办列表核心操作实现
impl TodoList {// 1. 从本地文件加载待办列表(文件不存在则返回空列表)pub fn load_from_file(file_path: &str) -> Result<Self, Box<dyn std::error::Error>> {if !Path::new(file_path).exists() {return Ok(Self { todos: Vec::new() });}let content = fs::read_to_string(file_path)?;let todo_list: Self = serde_json::from_str(&content)?;Ok(todo_list)}// 2. 保存待办列表到本地文件(JSON 格式,带缩进便于阅读)pub fn save_to_file(&self, file_path: &str) -> Result<(), Box<dyn std::error::Error>> {let content = serde_json::to_string_pretty(self)?;fs::write(file_path, content)?;Ok(())}// 3. 添加新待办(支持指定优先级,默认中优先级)pub fn add_todo(&mut self, content: String, priority: Priority) {let new_id = self.todos.last().map(|t| t.id + 1).unwrap_or(1);let new_todo = Todo {id: new_id,content,created_at: Utc::now(),status: TodoStatus::Pending,priority,};self.todos.push(new_todo);}// 4. 标记待办为已完成(通过 ID 查找,未找到则返回错误)pub fn complete_todo(&mut self, todo_id: usize) -> Result<(), String> {let todo = self.todos.iter_mut().find(|t| t.id == todo_id).ok_or_else(|| format!("未找到 ID 为 {} 的待办", todo_id))?;todo.status = TodoStatus::Completed;Ok(())}// 5. 删除待办(通过 ID 筛选,未找到则返回错误)pub fn delete_todo(&mut self, todo_id: usize) -> Result<(), String> {let initial_len = self.todos.len();self.todos.retain(|t| t.id != todo_id);if self.todos.len() == initial_len {return Err(format!("未找到 ID 为 {} 的待办", todo_id));}Ok(())}// 6. 显示所有待办(按优先级降序排序,含状态、优先级、时间信息)pub fn display(&self) {if self.todos.is_empty() {println!("🎉 暂无待办任务,快去添加一个吧!");return;}let mut sorted_todos = self.todos.clone();// 按优先级降序排序(High > Medium > Low)sorted_todos.sort_by(|a, b| b.priority.cmp(&a.priority));println!("📋 你的待办列表(共 {} 个任务,按优先级排序):", sorted_todos.len());for todo in sorted_todos {let status_icon = match todo.status {TodoStatus::Pending => "🔴",TodoStatus::Completed => "🟢",};let priority_icon = todo.priority.to_icon();let priority_str = todo.priority.to_str();// UTC 时间转本地时间let local_time = todo.created_at.with_timezone(&Local);let time_str = local_time.format("%Y-%m-%d %H:%M:%S").to_string();println!("{} {} ID: {} | 内容: {} | 优先级: {} | 创建时间: {}",status_icon, priority_icon, todo.id, todo.content, priority_str, time_str);}}// 7. 关键词搜索待办(不区分大小写,返回匹配结果)pub fn search(&self, keyword: &str) -> Vec<Todo> {let lower_keyword = keyword.to_lowercase();self.todos.iter().filter(|todo| todo.content.to_lowercase().contains(&lower_keyword)).cloned().collect()}// 8. 显示搜索结果(按优先级排序,空结果提示友好信息)pub fn display_search_results(&self, keyword: &str) {let results = self.search(keyword);if results.is_empty() {println!("🔍 未找到包含关键词「{}」的待办任务", keyword);return;}let mut sorted_results = results;sorted_results.sort_by(|a, b| b.priority.cmp(&a.priority));println!("🔍 找到 {} 个包含关键词「{}」的待办任务:",sorted_results.len(),keyword);for todo in sorted_results {let status_icon = match todo.status {TodoStatus::Pending => "🔴",TodoStatus::Completed => "🟢",};let priority_icon = todo.priority.to_icon();let priority_str = todo.priority.to_str();let local_time = todo.created_at.with_timezone(&Local);let time_str = local_time.format("%Y-%m-%d %H:%M:%S").to_string();println!("{} {} ID: {} | 内容: {} | 优先级: {} | 创建时间: {}",status_icon, priority_icon, todo.id, todo.content, priority_str, time_str);}}
}

4.2 命令行交互逻辑(src/main.rs)

use clap::Parser;
use rust_todo_cli::todo::{Priority, TodoList};// 定义命令行参数结构(支持 add/list/complete/delete/search 子命令)
#[derive(Parser, Debug)]
#[command(author, version, about = "Rust 命令行待办工具(支持优先级+搜索)", long_about = None)]
enum TodoCommand {/// 添加新待办(示例:todo add "买牛奶" -p High)Add {/// 待办任务内容content: String,/// 任务优先级(可选,默认 Medium)#[arg(short = 'p', long = "priority", default_value_t = Priority::Medium)]priority: Priority,},/// 查看所有待办(按优先级排序)List,/// 标记待办为已完成(示例:todo complete 1)Complete {/// 待办IDid: usize,},/// 删除待办(示例:todo delete 1)Delete {/// 待办IDid: usize,},/// 搜索待办(示例:todo search "Rust")Search {/// 搜索关键词(不区分大小写)keyword: String,},
}// 本地待办数据存储路径(当前目录下的 todo.json)
const TODO_FILE_PATH: &str = "todo.json";fn main() {// 解析命令行参数let command = TodoCommand::parse();// 加载本地待办列表(处理加载错误)let mut todo_list = match TodoList::load_from_file(TODO_FILE_PATH) {Ok(list) => list,Err(e) => {eprintln!("❌ 加载待办列表失败:{}", e);return;}};// 根据不同命令执行对应操作match command {TodoCommand::Add { content, priority } => {todo_list.add_todo(content.clone(), priority);if todo_list.save_to_file(TODO_FILE_PATH).is_ok() {println!("✅ 成功添加待办:{} | 优先级:{}",content, priority.to_str());} else {eprintln!("❌ 保存待办失败,请重试!");}}TodoCommand::List => {todo_list.display();}TodoCommand::Complete { id } => {match todo_list.complete_todo(id) {Ok(_) => {if todo_list.save_to_file(TODO_FILE_PATH).is_ok() {println!("✅ 成功标记 ID {} 为已完成!", id);} else {eprintln!("❌ 保存状态失败,请重试!");}}Err(e) => eprintln!("❌ 标记失败:{}", e),}}TodoCommand::Delete { id } => {match todo_list.delete_todo(id) {Ok(_) => {if todo_list.save_to_file(TODO_FILE_PATH).is_ok() {println!("✅ 成功删除 ID {} 的待办!", id);} else {eprintln!("❌ 保存删除结果失败,请重试!");}}Err(e) => eprintln!("❌ 删除失败:{}", e),}}TodoCommand::Search { keyword } => {todo_list.display_search_results(&keyword);}}
}

五、项目运行与测试

5.1 基本命令格式

所有命令需在 rust_todo_cli 项目目录下执行,格式为 cargo run -- 子命令 参数,各功能对应的命令如下:

功能命令示例说明
添加待办cargo run -- add "买牛奶"添加默认优先级(中)的待办
添加指定优先级待办cargo run -- add "完成 Rust 项目" -p High添加高优先级待办
查看待办cargo run -- list显示所有待办,按优先级降序排序
标记完成cargo run -- complete 1标记 ID 为 1 的待办为已完成
删除待办cargo run -- delete 1删除 ID 为 1 的待办
搜索待办cargo run -- search "Rust"搜索内容包含 “Rust” 的待办,不区分大小写

5.2 完整测试流程

步骤 1:添加不同优先级待办
# 添加高优先级待办
cargo run -- add "完成 Rust 命令行工具开发" -p High
# 添加中优先级待办(默认)
cargo run -- add "学习 Rust 序列化库 serde"
# 添加低优先级待办
cargo run -- add "整理项目文档" -p Low

执行后终端输出:

✅ 成功添加待办:完成 Rust 命令行工具开发 | 优先级:高
✅ 成功添加待办:学习 Rust 序列化库 serde | 优先级:中
✅ 成功添加待办:整理项目文档 | 优先级:低

在这里插入图片描述

步骤 2:查看待办列表(验证优先级排序)

执行查看命令:

cargo run -- list

输出结果(按优先级 High > Medium > Low 排序,含状态、优先级图标与时间):

📋 你的待办列表(共 3 个任务,按优先级排序):
🔴 🔴 ID: 1 | 内容: 完成 Rust 命令行工具开发 | 优先级: 高 | 创建时间: 2025-11-04 19:53:27
🔴 🟡 ID: 2 | 内容: 学习 Rust 序列化库 serde | 优先级: 中 | 创建时间: 2025-11-04 19:55:35
🔴 🔵 ID: 3 | 内容: 整理项目文档 | 优先级: 低 | 创建时间: 2025-11-04 19:55:45

说明:状态图标 “🔴” 表示未完成,优先级图标 “🔴/🟡/🔵” 对应高 / 中 / 低优先级,时间已转换为本地时区。
在这里插入图片描述

步骤 3:标记待办为已完成

执行标记命令(标记 ID=2 的待办):

cargo run -- complete 2

输出结果:

✅ 成功标记 ID 2 为已完成!

再次查看待办(cargo run -- list),ID=2 的状态图标变为 “🟢”:

📋 你的待办列表(共 3 个任务,按优先级排序):
🔴 🔴 ID: 1 | 内容: 完成 Rust 命令行工具开发 | 优先级: 高 | 创建时间: 2025-11-04 19:53:27
🟢 🟡 ID: 2 | 内容: 学习 Rust 序列化库 serde | 优先级: 中 | 创建时间: 2025-11-04 19:55:35
🔴 🔵 ID: 3 | 内容: 整理项目文档 | 优先级: 低 | 创建时间: 2025-11-04 19:55:45

在这里插入图片描述

步骤 4:测试搜索功能(不区分大小写)
场景 1:搜索包含 “Rust” 的待办

执行搜索命令:

cargo run -- search "Rust"

输出结果(匹配 ID=1 和 ID=2 的待办):

🔍 找到 2 个包含关键词「Rust」的待办任务:
🔴 🔴 ID: 1 | 内容: 完成 Rust 命令行工具开发 | 优先级: 高 | 创建时间: 2025-11-04 19:53:27
🟢 🟡 ID: 2 | 内容: 学习 Rust 序列化库 serde | 优先级: 中 | 创建时间: 2025-11-04 19:55:35

在这里插入图片描述

场景 2:搜索小写关键词 “rust”(验证不区分大小写)

执行搜索命令:

cargo run -- search "rust"

输出结果与场景 1 一致,说明搜索不区分大小写。

场景 3:搜索不存在的关键词(如 “Java”)

执行搜索命令:

cargo run -- search "Java"

输出友好提示:

🔍 未找到包含关键词「Java」的待办任务

在这里插入图片描述

步骤 5:删除待办

执行删除命令(删除 ID=3 的低优先级待办):

cargo run -- delete 3

输出结果:

✅ 成功删除 ID 3 的待办!

查看待办(cargo run -- list),ID=3 的待办已移除:

 你的待办列表(共 2 个任务,按优先级排序):
🔴 🔴 ID: 1 | 内容: 完成 Rust 命令行工具开发 | 优先级: 高 | 创建时间: 2025-11-04 19:53:27
🟢 🟡 ID: 2 | 内容: 学习 Rust 序列化库 serde | 优先级: 中 | 创建时间: 2025-11-04 19:55:35

在这里插入图片描述

5.3 数据持久化验证

待办数据存储在项目根目录的 todo.json 文件中,打开文件可查看 JSON 格式的持久化数据(带缩进,便于人类阅读):

{"todos": [{"id": 1,"content": "完成 Rust 命令行工具开发","created_at": "2025-11-04T11:53:27.248706900Z","status": "Pending","priority": "High"},{"id": 2,"content": "学习 Rust 序列化库 serde","created_at": "2025-11-04T11:55:35.909026300Z","status": "Completed","priority": "Medium"}]
}

在这里插入图片描述

关键验证点

  1. 重启终端或电脑后,重新执行 cargo run -- list,数据依然存在;

  2. 新增 / 删除 / 标记操作后,todo.json 会自动更新,确保数据与操作一致。

六、常见问题与解决方案

常见问题原因分析解决方案
执行 cargo run 报错 “找不到 todo 模块”未创建 src/lib.rs 或未导出 todo 模块确保 src/lib.rs 中存在 pub mod todo; 代码
添加待办时优先级参数不生效优先级参数值拼写错误(如 --priority high 写成 --priority hight优先级参数仅支持 Low/Medium/High(不区分大小写),检查拼写是否正确
搜索结果为空但待办存在关键词与待办内容完全不匹配,或存在特殊字符(如空格、符号)简化关键词(如用 “Rust” 代替 “Rust 开发”),或检查待办内容是否包含目标关键词
todo.json 文件损坏导致加载失败手动修改 todo.json 时格式错误(如缺少逗号、引号不匹配)删除损坏的 todo.json 文件,重新添加待办(或从备份恢复正确的 JSON 格式)
终端显示乱码(如图标不显示)终端不支持 Unicode 字符(如 Windows 旧版 CMD)改用 Windows Terminal 或 PowerShell,或在 todo.rs 中替换图标为文字(如 “高” 代替 “🔴”)

七、项目总结

7.1 核心技术收获

  1. Rust 语法实践:掌握结构体、枚举、 trait(如 Default/PartialOrd)、错误处理(Result 类型、? 操作符)的实际应用;

  2. 第三方库使用:熟悉 clap 命令行解析、serde JSON 序列化、chrono 时间处理的核心 API;

  3. 工程化思维:通过模块拆分(todo.rs 封装核心逻辑、main.rs 处理交互)实现代码解耦,通过 JSON 持久化确保数据安全。

7.2 工具使用场景

本工具适用于开发者日常任务管理,尤其是需要区分任务优先级(如 “紧急开发需求” 高优先级、“文档整理” 低优先级)的场景,命令行操作高效且不依赖图形界面,可集成到终端工作流中。

通过本项目的开发与优化,不仅能巩固 Rust 基础,还能培养 “从需求到实现” 的完整开发思维,为后续更复杂的 Rust 项目(如网络服务、桌面应用)打下基础。

了解更多相关知识可参考旋武开源社区

http://www.dtcms.com/a/606533.html

相关文章:

  • PANDA:通过代理型 AI 工程师迈向通用视频异常检测
  • 关于SSL/TLS证书的详细说明+即加密通信协议
  • 淘宝做问卷的网站好京东网上商城书店
  • 视频融合平台EasyCVR:构筑山洪灾害预警的“智慧耳目”与“决策大脑”
  • 自动优化网站建设电话wordpress vip解析插件
  • 【RPC:分布式跨节点透明通信协议】【Raft:简单易实现的分布式共识算法】
  • 做网站用什么编程网站建设管理是
  • 网站建设有证书吗喀什网站建设公司
  • 建设局网站公示的规划意味着什么成都微信小程序商城
  • thymeleaf模板引擎
  • Git 命令 作用、常用选项、示例、何时使用与注意事项指南
  • 太原制作网站企业更换网站服务器
  • 深入理解 Python 的属性化方法
  • 北京网站备案拍照的地点河北建设厅网站开通账号
  • AI Agent记忆系统深度实现:从短期记忆到长期人格的演进
  • APScheduler入门:轻松掌握Python任务调度
  • LLMs之 Ranking:OpenRouter LLM Rankings的简介、安装和使用方法、案例应用之详细攻
  • 算法题(Python)链表篇 | 3.翻转链表
  • 找个免费的网站这么难吗用jsp做的二手交易网站
  • 网站后台申请邮箱手机网站 方案
  • 新站突然网站停止收录给公司做个网站多少钱
  • 【C语言实战:实现数组的重复拼接(动态内存+指针参数详解)】
  • wordpress文章付费可看温岭新站seo
  • React zustand todos案例(带本地存储localStorage、persist)todoStore.ts
  • mac配置 unity+vscode的坑
  • 极速网站推广专家wordpress综合网
  • 上海定制网站建设公司网站域名什么意思
  • 【OpenCV + VS】直方图与模糊操作
  • 代码随想录 435.无重叠区间
  • 【AVL树与红黑树】:告别“瘸腿”树,体验平衡的艺术