Rust 实战项目:TODO 管理器

一、项目概述

项目简介

本项目是一款基于 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/),了解更多资讯~

相关推荐
Moonbit1 小时前
入围名单公布|2025 MGPIC 决赛即将拉开帷幕!
后端·算法
s9123601012 小时前
【Rust】使用lldb 调试core dump
前端·javascript·rust
爱吃烤鸡翅的酸菜鱼2 小时前
用【rust】实现命令行音乐播放器
开发语言·后端·rust
全栈陈序员2 小时前
用Rust和Bevy打造2D平台游戏原型
开发语言·rust·游戏引擎·游戏程序
黛琳ghz2 小时前
用 Rust 从零构建高性能文件加密工具
开发语言·后端·rust
悟世君子2 小时前
Rust 开发环境搭建
开发语言·后端·rust
OlahOlah2 小时前
Go 入门实战:音乐专辑管理 API
后端
DARLING Zero two♡2 小时前
用Rust构建一个OCR命令行工具
数据库·rust·ocr
代码狂想家2 小时前
Rust时序数据库实现:从压缩算法到并发优化的实战之旅
开发语言·rust·时序数据库