基于Rust的ToDo列表实现

Rust实现ToDo列表

基于Rust实现的终端ToDo学习项目。

初学者记录学习过程中遇到的各种问题和解决过程。

理解项目拆分任务

ToDo的主要实现功能有:

  • 添加代办
  • 修改代办
  • 删除代办
  • 完成代办
    看似简单,上手发现很多细节有问题。

对于这些操作,都是基于任务或任务列表的操作,使用面向对象 开发。

我将项目拆分了两个对象

  • 构建Tasks结构体,主要针对任务内容性的构建,记录任务内容,时间,重要程度,完成与否等信息,面向这些信息创建的功能对于修改这些信息的,有创建,修改,为了避免代码重复,创建了输入内容和等级的方法以及验证。
  • 构建ToDoManager管理结构体,记录任务ID和Tasks的HashMap,ID自增关联Tasks结构体做哈希表的键值对,对于少量数据的查询相当快,对于这个结构体,他实现的一般是新增,删除,列出等功能,相较宏观对任务进行的操作。

主要的功能和对象创造完成,下一步是实现这些功能,将这些方法穿起来。

流程开发

这一步根据项目流程逻辑,将面向对象创建的方法,通过面向过程进行串联。

  • 添加代办,调用输入函数->Tasks下的创建函数->创建Tasks结构体->添加到ToDoManager结构体下的哈希表中
  • 修改代办,调取任务ID->调用输入函数(函数下存在传入可选修改,可适配添加和修改函数)->调哈希表->更新数据
  • 删除代办,调任务ID->清理哈希表
    将这些流程操作单独封装为函数,以便于进行跳转。

页面配置

不能使用函数剑的调用,会导致重复递归和嵌套。

使用状态机,利用枚举实现各个页面功能。跳转逻辑更加清晰。

rust 复制代码
#[derive(PartialEq)]
enum PageState{
    Main,
    Add,
    Modify,
    Delete,
    Exit,
}
......
while default_state != PageState::Exit{
        default_state =match default_state{
            PageState::Main => todo_main(&mut todo, terminal_width),
            PageState::Add => todo_add(&mut todo, terminal_width),
            PageState::Delete => todo_delete(&mut todo, terminal_width),
            PageState::Modify => todo_modify(&mut todo, terminal_width),
            PageState::Exit => PageState::Exit,
            };
}

这些不同函数中,输入不同字符进行返回不同枚举类型(例如输入1添加任务),返回枚举后重新while判断。

细节处理

  • 格式化打印,terminal_size::{terminal_size,Width};获取命令行宽度。
  • 清除命令行,清理其他内容println!("\x1B[2J\x1B[1;1H");
  • 不同等级颜色高光,"\x1b[91m""\x1b[0m"中间夹着的地方进行改变颜色,91亮红。
  • 时间获取,先通过标准库获取时间戳,chrono::{DateTime, Local};对时间戳进行转换。
  • 退出/跳转,获取q字母进行返回PageState::Main或退出。
  • 重要等级排列,遍历收集到vec存储,降序排列.sort_by(|a, b| b.1.level.cmp(&a.1.level));,使用依然是引用,但足以,主要以完成目标为主,过早优化是大忌。

待开发部分:

  • 数据持久化
  • 标记完成和完成任务的记录(终端的ToDo列表实用性并不大,主要理解项目的逻辑)

悟道

项目的开发经历了数小时,查阅标准库,代码补全,问AI帮助了我很多,帮我选择库,帮我查出一些细节的实现。

AI的促进学习很大的体现,极大程度辅助了开发,各种库和知识都是在学习和写代码中逐步记忆,而AI的出现可以帮助我们去节省寻找实现方案的时间。只有真带着项目学习,才能学习到教程中没有涉及的部分。

理论->实践->理论 的学习过程,才能真正帮助深刻学习。

写出代码并不是很重要,重要的是理解项目逻辑,用更加清晰的逻辑拆分项目。

参考代码实现

后续都将上传我的Blog

ini 复制代码
实现库
[dependencies]
terminal_size = "0.3"
chrono = "0.4"
rust 复制代码
use std::time::SystemTime;
use std::collections::HashMap;
use std::io;
use terminal_size::{terminal_size,Width};
use chrono::{DateTime, Local};

#[derive(PartialEq)]
enum PageState{
    Main,
    Add,
    Modify,
    Delete,
    Exit,
}

fn format_time(time: &SystemTime) -> String {
    let datetime: DateTime<Local> = time.clone().into();
    
    datetime.format("%Y-%m-%d %H:%M").to_string()
}
fn get_color(level: u8) -> String{
    let color = match level{
        1 => "\x1b[97m",
        2 => "\x1b[94m",
        3 => "\x1b[93m",
        4 => "\x1b[95m",
        5 => "\x1b[91m",
        _ => "\x1b[0m",
    };
    color.to_string()
}
struct Tasks{
    name: String,
    time: SystemTime,
    finish: bool,
    level: u8,
}

impl Tasks{
    fn new(name: String, level: u8) -> Result<Tasks, String>{
        fn get_time() -> SystemTime{
            SystemTime::now()
        }
        Ok(Tasks{
            name,
            time: get_time(),
            finish: false,
            level,
        })
    }

    fn modify(&mut self, name: String, level: u8){
        self.name = name;
        self.level = level;
    }

    // fn mark_done(&mut self) -> bool{
    //     self.finish = true;
    //     self.finish
    // }

    fn input_name(may_name: Option<String>) -> String{
        let mut name = String::new();
        io::stdin().read_line(&mut name).expect("读取失败");
        if name.trim().is_empty(){
            name = may_name.unwrap_or("".to_string());
        }
        name.trim().to_string()
    }
    fn input_level(may_level: Option<u8>) ->u8{
        let mut level:u8 = 0;
        while !(level >= 1 && level <= 5){
            println!("等级必须在 1-5 之间");
            let mut input = String::new();
            io::stdin().read_line(&mut input).expect("读取失败");
            if input.is_empty(){
                level = may_level.unwrap_or(0);
                break;
            }
            level = input.trim().parse().unwrap_or(0);
        }
        return level;
    }
}

struct TodoManager{
    id: u8,
    tasks: HashMap<u8,Tasks>,
}

impl TodoManager{
    fn new() -> TodoManager{
        TodoManager{
            id: 1,
            tasks: HashMap::new(),
        }
    }
    fn add_task(&mut self, name: String, level: u8){
        let task = Tasks::new(name, level);
        self.tasks.insert(self.id, task.unwrap());
        self.id += 1;
    }
    fn delete_task(&mut self, id: u8){
        self.tasks.remove(&id);
    }
    fn list_tasks(&self){
        let mut tasks_vec: Vec<_> = self.tasks.iter().collect();
        tasks_vec.sort_by(|a, b| b.1.level.cmp(&a.1.level));
        for (id, task) in tasks_vec{
            let color = get_color(task.level);
            let time = format_time(&task.time);
            println!("{}: {}任务:{}  等级:{}  时间:{}  是否完成: {}{}",  id, color, task.name, task.level, time, task.finish, "\x1b[0m");
        }
    }

}


fn todo_main(todo: &mut TodoManager , terminal_width: usize) -> PageState{
    println!("{}", "=".repeat(terminal_width));
    println!("{:^width$}", "ToDo List", width = terminal_width);
    println!("目前待办列表为:");
    todo.list_tasks();
    println!("{}", "=".repeat(terminal_width));
    let col_width = terminal_width / 4;
    println!("{:<col_width$}{:<col_width$}{:<col_width$}{:<col_width$}", 
             "1. 添加任务", "2. 修改任务", "3. 删除任务", "q. 退出", 
             col_width = col_width);
    let mut state = String::new();
    io::stdin().read_line(&mut state).expect("读取失败");
    match state.trim(){
        "1" => PageState::Add,
        "2" => PageState::Modify,
        "3" => PageState::Delete,
        "q" => PageState::Exit,
        _ => {
            println!("输入无效");
            PageState::Main
        }
    }
}


fn todo_add(todo: &mut TodoManager, terminal_width: usize) -> PageState{
    println!("{}", "=".repeat(terminal_width));
    println!("{:^width$}", "ToDo List", width = terminal_width);
    println!("添加任务内容");
    let name = Tasks::input_name(None);
    println!("添加任务等级");
    let level = Tasks::input_level(None);
    todo.add_task(name, level);
    PageState::Main
}

fn todo_modify(todo: &mut TodoManager, terminal_width: usize) -> PageState{
    println!("{}", "=".repeat(terminal_width));
    println!("{:^width$}", "ToDo List", width = terminal_width);
    println!("输入要修改的任务ID");
    todo.list_tasks();
    println!("{}", "=".repeat(terminal_width));
    println!("输入q返回上一级");

    let mut inputid: u8 = 0;
    while !todo.tasks.contains_key(&inputid){
        let mut tryinputid = String::new();
        io::stdin().read_line(&mut tryinputid).expect("读取失败");
        if tryinputid.trim() == "q"{
            return PageState::Main;
        }
        inputid = tryinputid.trim().parse().unwrap_or(0);
    }
   
    println!("输入改动的任务名称");
    let name = Tasks::input_name(Some(todo.tasks.get(&inputid).unwrap().name.clone()));
    
    println!("输入改动的任务等级");
    let level = Tasks::input_level(Some(todo.tasks.get(&inputid).unwrap().level));
    todo.tasks.get_mut(&inputid).unwrap().modify(name, level);
    PageState::Main
}



fn todo_delete(todo: &mut TodoManager, terminal_width: usize) -> PageState{
    println!("{}", "=".repeat(terminal_width));
    println!("{:^width$}", "ToDo List", width = terminal_width);
    println!("输入要删除的任务ID");
    todo.list_tasks();
    println!("{}", "=".repeat(terminal_width));
    println!("输入q返回上一级");
    let mut id: u8 = 0;
    while !todo.tasks.contains_key(&id){
        let mut tryid = String::new();
        io::stdin().read_line(&mut tryid).expect("读取失败");
        if tryid.trim() == "q"{
            return PageState::Main;
        }
        id = tryid.trim().parse().unwrap_or(0);
    }
    todo.delete_task(id);
    PageState::Main
}

fn get_terminal_width() -> usize{
    terminal_size().map(|(Width(w), _)| w as usize).unwrap_or(80)
}

fn main(){
    let terminal_width = get_terminal_width();
    let mut todo = TodoManager::new();
    todo.add_task("学习Rust".to_string(), 5);
    todo.add_task("学习AI".to_string(), 4);

    let mut default_state: PageState = PageState::Main;
    println!("\x1B[2J\x1B[1;1H");
    while default_state != PageState::Exit{
        default_state =match default_state{
            PageState::Main => todo_main(&mut todo, terminal_width),
            PageState::Add => todo_add(&mut todo, terminal_width),
            PageState::Delete => todo_delete(&mut todo, terminal_width),
            PageState::Modify => todo_modify(&mut todo, terminal_width),
            PageState::Exit => PageState::Exit,
        };
        println!("\x1B[2J\x1B[1;1H");
    }
}
相关推荐
skilllite作者1 小时前
SkillLite Rust 沙箱与 AI Agent 自进化实战指南
开发语言·人工智能·后端·架构·rust
ejinxian3 小时前
Rust Web框架三巨头Actix-web、Axum 、Rocket
开发语言·后端·rust
ithadoop16 小时前
Solana入门:区块链新手速成指南(第二阶段:开发入门)
rust·web3·区块链·智能合约·solana
Rust语言中文社区17 小时前
【Rust日报】2026-04-24 Vizia 0.4 发布——纯 Rust 声明式响应式 GUI 框架
开发语言·后端·rust
techdashen1 天前
用自家产品构建自家产品:Cloudflare Images 的工程架构解析
开发语言·架构·rust
恋喵大鲤鱼1 天前
RUST 的特色概念与 Go 到 Rust 的思维模式转变
rust
光影少年1 天前
vite+rust生态链工具链
开发语言·前端·后端·rust·前端框架
techdashen1 天前
服务不停,升级照常:Cloudflare 是怎么做到零中断重启的
开发语言·rust