Files
Learn-Rust-through-projects/src/main.rs

141 lines
3.4 KiB
Rust

use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs;
use std::path::Path;
#[derive(Parser)]
#[command(name = "todo")]
#[command(about = "A simple todo list manager")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Add { task: String },
List,
Complete { id: usize },
Remove { id: usize },
}
#[derive(Serialize, Deserialize, Debug)]
struct Task {
id: usize,
description: String,
completed: bool,
}
#[derive(Serialize, Deserialize, Debug)]
struct TodoList {
tasks: Vec<Task>,
next_id: usize,
}
#[derive(Debug)]
enum TodoError {
FileError(String),
ParseError(String),
TaskNotFound(usize),
InvalidInput(String),
}
impl fmt::Display for TodoError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TodoError::FileError(msg) => write!(f, "File error: {}", msg),
TodoError::ParseError(msg) => write!(f, "Parse error: {}", msg),
TodoError::TaskNotFound(id) => write!(f, "Task {} not found", id),
TodoError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
}
}
}
impl std::error::Error for TodoError {}
impl TodoList {
fn new() -> Self {
TodoList {
tasks: Vec::new(),
next_id: 1,
}
}
fn load_from_file(filename: &str) -> Result<Self, Box<dyn std::error::Error>> {
if Path::new(filename).exists() {
let contents = fs::read_to_string(filename)?;
let todo_list = serde_json::from_str(&contents)?;
Ok(todo_list)
} else {
Ok(TodoList::new())
}
}
fn save_to_file(&self, filename: &str) -> Result<(), Box<dyn std::error::Error>> {
let json = serde_json::to_string_pretty(self)?;
fs::write(filename, json)?;
Ok(())
}
fn add_task(&mut self, description: String) -> Result<(), TodoError> {
if description.trim().is_empty() {
return Err(TodoError::InvalidInput(
"Task description cannot be empty".to_string(),
));
}
let task = Task {
id: self.next_id,
description,
completed: false,
};
self.tasks.push(task);
self.next_id += 1;
Ok(())
}
fn complete_task(&mut self, id: usize) -> Result<(), TodoError> {
match self.tasks.iter_mut().find(|task| task.id == id) {
Some(task) => {
task.completed = true;
Ok(())
}
None => Err(TodoError::TaskNotFound(id)),
}
}
}
fn main() {
let cli = Cli::parse();
if let Err(e) = run_command(cli) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
fn run_command(cli: Cli) -> Result<(), TodoError> {
let mut todo_list =
TodoList::load_from_file("tasks.json").map_err(|e| TodoError::FileError(e.to_string()))?;
match cli.command {
Commands::Add { task } => {
todo_list.add_task(task)?;
println!("Task added successfully");
}
Commands::Complete { id } => {
todo_list.complete_task(id)?;
println!("Task {} marked as complete", id);
}
_ => {} // Handle other commands
}
todo_list
.save_to_file("todo.json")
.map_err(|e| TodoError::FileError(e.to_string()))?;
Ok(())
}