一、项目概述
项目简介
本项目是一款基于 Rust 开发的轻量级命令行 TODO 管理工具,支持任务的增删改查、状态标记、优先级设置及数据持久化等核心功能。工具仅依赖 Rust 标准库,具有跨平台兼容性强、操作高效、数据安全等特点,适合开发者通过终端快速管理日常任务。
核心功能
- 任务管理:支持添加、删除、修改、查询 TODO 任务;
- 状态管理:可标记任务为 "待办"、"进行中"、"已完成" 状态;
- 优先级设置:支持高 / 中 / 低三级优先级标记;
- 数据持久化:采用 JSON 格式本地存储,确保数据不丢失;
- 筛选功能:可按状态、优先级筛选任务,支持关键词搜索。
技术栈
- 开发语言:Rust(Edition 2021);
- 标准库模块:
<font style="background-color:rgb(187,191,196);">std::fs</font>(文件操作)、<font style="background-color:rgb(187,191,196);">std::io</font>(输入输出)、<font style="background-color:rgb(187,191,196);">serde</font>与<font style="background-color:rgb(187,191,196);">serde_json</font>(JSON 序列化 / 反序列化)。
二、环境准备
安装 Rust 工具链
前往 Rust 官网 下载 <font style="background-color:rgb(187,191,196);">rustup-init</font>,默认安装即可(包含 <font style="background-color:rgb(187,191,196);">rustc</font> 编译器与 <font style="background-color:rgb(187,191,196);">cargo</font> 包管理器)。安装完成后,终端执行以下命令验证:
安装 Rust 工具链
前往 Rust 官网 下载 <font style="background-color:rgb(187,191,196);">rustup-init</font>,默认安装即可(包含 <font style="background-color:rgb(187,191,196);">rustc</font> 编译器与 <font style="background-color:rgb(187,191,196);">cargo</font> 包管理器)。安装完成后,终端执行以下命令验证:
plaintext
plain
rustc --version # 需 ≥1.70.0
cargo --version
创建项目
终端执行以下命令创建项目并进入目录:
plaintext
plain
cargo new rust_todo_cli
cd rust_todo_cli
配置依赖
修改项目根目录的 <font style="background-color:rgb(187,191,196);">Cargo.toml</font> 文件,添加 JSON 处理依赖:
plaintext
plain
[package]
name = "rust_todo_cli"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
三、项目结构
项目采用单文件模块化设计,所有逻辑集中在 <font style="background-color:rgb(187,191,196);">src/main.rs</font> 中,结构清晰且易于维护:
plaintext
plain
rust_todo_cli/
├── Cargo.toml # 项目配置与依赖管理
├── todo_data.json # 任务数据存储文件(运行后自动生成)
└── src/
└── main.rs # 核心逻辑(命令解析、任务处理、数据持久化)
四、核心代码实现
完整代码(src/main.rs)
plain
use std::fs::{self, File};
use std::io::{self, BufRead, Write};
use serde::{Serialize, Deserialize};
use std::path::Path;
// 任务状态枚举
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
enum TaskStatus {
#[serde(rename = "todo")]
ToDo,
#[serde(rename = "doing")]
Doing,
#[serde(rename = "done")]
Done,
}
// 优先级枚举
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
enum Priority {
#[serde(rename = "high")]
High,
#[serde(rename = "medium")]
Medium,
#[serde(rename = "low")]
Low,
}
// 任务结构体
#[derive(Debug, Serialize, Deserialize, Clone)]
struct Task {
id: u32,
title: String,
description: String,
status: TaskStatus,
priority: Priority,
created_at: String,
}
// 应用状态结构体
struct AppState {
tasks: Vec<Task>,
data_path: String,
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let data_path = "todo_data.json".to_string();
let mut app_state = AppState {
tasks: load_tasks(&data_path).unwrap_or_default(),
data_path: data_path.clone(),
};
if args.len() < 2 {
print_usage();
return;
}
match args[1].as_str() {
"add" => handle_add(&mut app_state, &args[2..]),
"list" => handle_list(&app_state, &args[2..]),
"update" => handle_update(&mut app_state, &args[2..]),
"delete" => handle_delete(&mut app_state, &args[2..]),
"complete" => handle_complete(&mut app_state, &args[2..]),
"search" => handle_search(&app_state, &args[2..]),
"help" => print_usage(),
_ => {
eprintln!("未知命令: {}", args[1]);
print_usage();
}
}
// 保存任务数据
if let Err(e) = save_tasks(&app_state.tasks, &data_path) {
eprintln!("保存数据失败: {}", e);
}
}
// 加载任务数据
fn load_tasks(path: &str) -> Result<Vec<Task>, String> {
if !Path::new(path).exists() {
return Ok(Vec::new());
}
let content = fs::read_to_string(path)
.map_err(|e| format!("读取文件失败: {}", e))?;
serde_json::from_str(&content)
.map_err(|e| format!("解析数据失败: {}", e))
}
// 保存任务数据
fn save_tasks(tasks: &[Task], path: &str) -> Result<(), String> {
let json = serde_json::to_string_pretty(tasks)
.map_err(|e| format!("序列化失败: {}", e))?;
fs::write(path, json)
.map_err(|e| format!("写入文件失败: {}", e))
}
// 打印使用帮助
fn print_usage() {
println!("TODO 命令行管理器 使用指南:");
println!(" todo <命令> [参数]");
println!();
println!("命令列表:");
println!(" add <标题> [描述] - 添加新任务 (优先级默认为 medium)");
println!(" 选项: --priority high|medium|low");
println!(" list - 列出所有任务");
println!(" 选项: --status todo|doing|done");
println!(" --priority high|medium|low");
println!(" update <id> <新标题> [新描述] - 更新任务");
println!(" delete <id> - 删除任务");
println!(" complete <id> - 将任务标记为已完成");
println!(" search <关键词> - 搜索任务标题或描述");
println!(" help - 显示帮助信息");
println!();
println!("示例:");
println!(" 添加高优先级任务: todo add \"完成 Rust 项目\" \"实现所有核心功能\" --priority high");
println!(" 列出所有待办任务: todo list --status todo");
println!(" 标记任务 1 为已完成: todo complete 1");
}
// 处理添加任务
fn handle_add(app_state: &mut AppState, args: &[String]) {
if args.is_empty() {
eprintln!("请提供任务标题");
return;
}
let mut title = args[0].clone();
let mut description = String::new();
let mut priority = Priority::Medium;
let mut i = 1;
// 解析描述和优先级参数
while i < args.len() {
if args[i] == "--priority" && i + 1 < args.len() {
priority = match args[i + 1].as_str() {
"high" => Priority::High,
"medium" => Priority::Medium,
"low" => Priority::Low,
_ => {
eprintln!("无效的优先级,使用默认值 medium");
Priority::Medium
}
};
i += 2;
} else {
if !description.is_empty() {
description.push(' ');
}
description.push_str(&args[i]);
i += 1;
}
}
// 生成任务 ID
let id = app_state.tasks.iter()
.map(|t| t.id)
.max()
.unwrap_or(0) + 1;
// 获取当前时间
let created_at = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
let task = Task {
id,
title,
description,
status: TaskStatus::ToDo,
priority,
created_at,
};
app_state.tasks.push(task.clone());
println!("✅ 添加任务成功 (ID: {})", task.id);
}
// 处理列出任务
fn handle_list(app_state: &AppState, args: &[String]) {
let mut status_filter: Option<TaskStatus> = None;
let mut priority_filter: Option<Priority> = None;
let mut i = 0;
// 解析筛选参数
while i < args.len() {
match args[i].as_str() {
"--status" => {
if i + 1 < args.len() {
status_filter = match args[i + 1].as_str() {
"todo" => Some(TaskStatus::ToDo),
"doing" => Some(TaskStatus::Doing),
"done" => Some(TaskStatus::Done),
_ => {
eprintln!("无效的状态筛选条件");
return;
}
};
i += 2;
} else {
eprintln!("缺少状态参数");
return;
}
}
"--priority" => {
if i + 1 < args.len() {
priority_filter = match args[i + 1].as_str() {
"high" => Some(Priority::High),
"medium" => Some(Priority::Medium),
"low" => Some(Priority::Low),
_ => {
eprintln!("无效的优先级筛选条件");
return;
}
};
i += 2;
} else {
eprintln!("缺少优先级参数");
return;
}
}
_ => {
eprintln!("未知参数: {}", args[i]);
return;
}
}
}
// 筛选任务
let filtered_tasks: Vec<&Task> = app_state.tasks.iter()
.filter(|t| status_filter.as_ref().map_or(true, |s| &t.status == s))
.filter(|t| priority_filter.as_ref().map_or(true, |p| &t.priority == p))
.collect();
if filtered_tasks.is_empty() {
println!("📋 没有匹配的任务");
return;
}
println!("📋 任务列表 (共 {} 项):", filtered_tasks.len());
println!("----------------------------------------------------------------------");
println!("ID | 状态 | 优先级 | 创建时间 | 标题");
println!("----------------------------------------------------------------------");
for task in filtered_tasks {
let status_str = match task.status {
TaskStatus::ToDo => "待办",
TaskStatus::Doing => "进行中",
TaskStatus::Done => "已完成",
};
let priority_str = match task.priority {
Priority::High => "高",
Priority::Medium => "中",
Priority::Low => "低",
};
println!(
"{:<3} | {:<5} | {:<5} | {} | {}",
task.id, status_str, priority_str, task.created_at, task.title
);
}
}
// 处理更新任务
fn handle_update(app_state: &mut AppState, args: &[String]) {
if args.len() < 2 {
eprintln!("请提供任务 ID 和新标题");
return;
}
let id = match args[0].parse::<u32>() {
Ok(num) => num,
Err(_) => {
eprintln!("无效的 ID 格式");
return;
}
};
let task_index = match app_state.tasks.iter().position(|t| t.id == id) {
Some(idx) => idx,
None => {
eprintln!("未找到 ID 为 {} 的任务", id);
return;
}
};
let new_title = args[1].clone();
let new_description = args[2..].join(" ");
app_state.tasks[task_index].title = new_title;
app_state.tasks[task_index].description = new_description;
println!("✏️ 更新任务成功 (ID: {})", id);
}
// 处理删除任务
fn handle_delete(app_state: &mut AppState, args: &[String]) {
if args.is_empty() {
eprintln!("请提供任务 ID");
return;
}
let id = match args[0].parse::<u32>() {
Ok(num) => num,
Err(_) => {
eprintln!("无效的 ID 格式");
return;
}
};
let initial_len = app_state.tasks.len();
app_state.tasks.retain(|t| t.id != id);
if app_state.tasks.len() < initial_len {
println!("🗑️ 删除任务成功 (ID: {})", id);
} else {
eprintln!("未找到 ID 为 {} 的任务", id);
}
}
// 处理标记任务为已完成
fn handle_complete(app_state: &mut AppState, args: &[String]) {
if args.is_empty() {
eprintln!("请提供任务 ID");
return;
}
let id = match args[0].parse::<u32>() {
Ok(num) => num,
Err(_) => {
eprintln!("无效的 ID 格式");
return;
}
};
match app_state.tasks.iter_mut().find(|t| t.id == id) {
Some(task) => {
task.status = TaskStatus::Done;
println!("✅ 标记任务为已完成 (ID: {})", id);
}
None => eprintln!("未找到 ID 为 {} 的任务", id),
}
}
// 处理搜索任务
fn handle_search(app_state: &AppState, args: &[String]) {
if args.is_empty() {
eprintln!("请提供搜索关键词");
return;
}
let keyword = args[0].to_lowercase();
let matched_tasks: Vec<&Task> = app_state.tasks.iter()
.filter(|t| t.title.to_lowercase().contains(&keyword) ||
t.description.to_lowercase().contains(&keyword))
.collect();
if matched_tasks.is_empty() {
println!("🔍 未找到包含关键词 '{}' 的任务", keyword);
return;
}
println!("🔍 搜索结果 (共 {} 项):", matched_tasks.len());
println!("----------------------------------------------------------------------");
println!("ID | 状态 | 标题");
println!("----------------------------------------------------------------------");
for task in matched_tasks {
let status_str = match task.status {
TaskStatus::ToDo => "待办",
TaskStatus::Doing => "进行中",
TaskStatus::Done => "已完成",
};
println!("{:<3} | {:<5} | {}", task.id, status_str, task.title);
}
}
五、使用指南
基本命令格式
plaintext
plain
cargo run -- <命令> [参数]
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| add | 命令 | 添加新任务,需指定标题和可选描述 |
| list | 命令 | 列出任务,支持按状态和优先级筛选 |
| update | 命令 | 更新任务,需指定任务 ID 和新内容 |
| delete | 命令 | 删除任务,需指定任务 ID |
| complete | 命令 | 标记任务为已完成,需指定任务 ID |
| search | 命令 | 搜索任务,需指定关键词 |
| --status | 选项 | 筛选状态(todo/doing/done) |
| --priority | 选项 | 筛选或设置优先级(high/medium/low) |
功能测试
基本操作流程
bash
plain
# 添加任务cargo run -- add "学习 Rust 基础语法" "掌握变量、函数和控制流" --priority high
cargo run -- add "实现 TODO 管理器" "完成核心功能开发" --priority high
cargo run -- add "编写文档" "" --priority medium
# 列出所有任务cargo run -- list
# 标记任务为已完成cargo run -- complete 1# 按状态筛选任务cargo run -- list --status todo
# 搜索任务cargo run -- search "Rust"# 更新任务cargo run -- update 2 "完善 TODO 管理器" "添加筛选和搜索功能"# 删除任务cargo run -- delete 3




plain
cargo run -- complete 1

plain
cargo run -- search "Rust"

plain
cargo run -- update 2 "完善 TODO 管理器" "添加筛选和搜索功能"

plain
cargo run -- delete 3

输出示例
添加任务成功时的输出:
plaintext
plain
✅ 添加任务成功 (ID: 1)
列出任务时的输出:
plaintext
plain
📋 任务列表 (共 2 项):
----------------------------------------------------------------------
ID | 状态 | 优先级 | 创建时间 | 标题
----------------------------------------------------------------------
1 | 已完成 | 高 | 2023-10-15 10:30:00 | 学习 Rust 基础语法
2 | 待办 | 高 | 2023-10-15 10:31:00 | 完善 TODO 管理器
六、功能扩展方向
基础扩展(已实现)
- 任务状态管理:待办、进行中、已完成三种状态;
- 优先级设置:高、中、低三级优先级;
- 数据持久化:使用 JSON 格式存储任务数据。
进阶扩展建议
- 日期管理:添加任务截止日期,支持按日期筛选;
- 分类管理:支持为任务添加标签,实现分类管理;
- 统计功能:生成任务完成情况统计报告;
- 交互模式:支持进入交互模式,避免重复输入命令;
- 数据同步:支持将数据同步到云端或其他设备;
- 提醒功能:为任务设置提醒时间,到期提醒。
七、技术要点总结
核心技术亮点
(1)数据持久化设计
- 使用
<font style="background-color:rgb(187,191,196);">serde</font>和<font style="background-color:rgb(187,191,196);">serde_json</font>实现任务数据的序列化和反序列化,确保数据能够持久存储; - 采用 JSON 格式存储,既保证了数据的可读性,又便于后续扩展和迁移。
(2)命令行交互设计
- 清晰的命令结构,类似 Git 等常用工具的命令风格,降低学习成本;
- 支持多种筛选和查询方式,满足不同场景下的使用需求。
(3)类型安全与错误处理
- 使用枚举类型定义任务状态和优先级,确保类型安全;
- 完善的错误处理机制,对用户输入错误和文件操作错误进行友好提示。
(4)用户体验优化
- 格式化输出任务列表,信息展示清晰;
- 提供详细的帮助信息和使用示例,降低使用门槛。
性能优化思路
- 延迟加载:仅在需要时加载任务数据,减少内存占用;
- 增量更新:优化数据保存逻辑,仅在数据发生变化时进行写入;
- 索引优化:为常用查询字段建立索引,提高筛选和搜索效率。
Rust 语言特性应用
- 所有权与借用:合理使用所有权和借用规则,确保内存安全;
- 模式匹配 :使用
<font style="background-color:rgb(187,191,196);">match</font>表达式处理命令解析和状态转换,逻辑清晰; - 迭代器:充分利用迭代器和闭包进行数据筛选和转换,代码简洁高效;
- 结构体与枚举:通过结构体和枚举清晰定义数据模型,提高代码可维护性。
想了解更多关于Rust语言的知识及应用,可前往华为开放原子旋武开源社区(https://xuanwu.openatom.cn/),了解更多资讯~