cmc/learn
My personal learning area.
clone: git clone https://gitbay.org/cmc/learn.git
main: rust/todo/src/main.rs · raw
1use clap::{Arg, Command};
2use std::fs::{self, OpenOptions};
3use std::io::{self, Write};
4use std::path::Path;
5
6#[derive(Debug)]
7struct Task {
8 id: usize,
9 description: String,
10 completed: bool,
11}
12
13impl Task {
14 fn to_string(&self) -> String {
15 let status = if self.completed { "[X]" } else { "[ ]" };
16 format!("{} {}", status, self.description)
17 }
18}
19
20fn main() {
21 let matches = Command::new("Task Echo")
22 .version("1.0")
23 .about("Echoes tasks to a file")
24 .arg(
25 Arg::new("task")
26 .short('t')
27 .long("task")
28 .value_name("TASK")
29 .help("Sets the task to be saved")
30 .num_args(1..),
31 )
32 .arg(
33 Arg::new("done")
34 .short('d')
35 .long("done")
36 .value_name("TASK_ID")
37 .help("Marks the specified task(s) as complete")
38 .num_args(1..),
39 )
40 .get_matches();
41
42 let file_path = "tasks.txt";
43
44 // Handle task completion
45 if let Some(ids) = matches.get_many::<String>("done") {
46 let mut tasks = load_tasks(file_path);
47 let mut completed_tasks = Vec::new();
48 for id in ids {
49 if let Ok(index) = id.parse::<usize>() {
50 if index > 0 && index <= tasks.len() {
51 tasks[index - 1].completed = true; // Mark task as complete
52 } else {
53 completed_tasks.push(id.clone()); // Collect invalid task IDs
54 }
55 }
56 }
57 save_tasks(file_path, &tasks);
58
59 // Alert for nonexistent tasks
60 if !completed_tasks.is_empty() {
61 for id in completed_tasks {
62 println!("Task {} doesn't exist.", id);
63 }
64 } else {
65 println!("Tasks marked as complete.");
66 }
67
68 // Display the updated task list
69 display_tasks(file_path);
70 }
71
72 // Handle new tasks
73 if let Some(new_tasks) = matches.get_many::<String>("task") {
74 let mut tasks = load_tasks(file_path);
75 let next_id = tasks.len() + 1; // Determine the next task ID
76 for task_desc in new_tasks {
77 tasks.push(Task {
78 id: next_id,
79 description: task_desc.to_string(),
80 completed: false,
81 });
82 }
83 save_tasks(file_path, &tasks);
84 println!("Tasks saved to {}", file_path);
85
86 // Display the updated task list
87 display_tasks(file_path);
88 }
89
90 // Display saved tasks if no new tasks or done tasks were provided
91 if !matches.contains_id("task") && !matches.contains_id("done") {
92 display_tasks(file_path);
93 }
94}
95
96fn load_tasks(file_path: &str) -> Vec<Task> {
97 let mut tasks = Vec::new();
98 if Path::new(file_path).exists() {
99 let contents = fs::read_to_string(file_path).expect("Unable to read file");
100 for (id, line) in contents.lines().enumerate() {
101 let completed = line.starts_with("[X]");
102 let description = line[4..].trim().to_string();
103 tasks.push(Task {
104 id: id + 1,
105 description,
106 completed,
107 });
108 }
109 }
110 tasks
111}
112
113fn save_tasks(file_path: &str, tasks: &[Task]) {
114 let mut file = OpenOptions::new()
115 .create(true)
116 .write(true)
117 .truncate(true) // Clear the file before writing
118 .open(file_path)
119 .expect("Unable to open file");
120
121 for task in tasks {
122 writeln!(file, "{}", task.to_string()).expect("Unable to write to file");
123 }
124}
125
126fn display_tasks(file_path: &str) {
127 let tasks = load_tasks(file_path);
128 if tasks.is_empty() {
129 println!("No tasks saved yet.");
130 } else {
131 println!("Saved tasks:");
132 for task in tasks {
133 // Pad the task ID to a width of 3 for alignment
134 println!("{:>3}: {}", task.id, task.to_string()); // Display task ID
135 }
136 }
137}