1use clap::{Args as ClapArgs, Parser, Subcommand};
2use cli_table::{print_stdout, Cell, Color, Style, Table};
3use sublime_fuzzy::best_match;
4
5use crate::{
6 files::{
7 check_existing_metadata, create_metadata, enter_data_to_file, read_data_from_file,
8 remove_metadata,
9 },
10 format_date,
11 state::State,
12 tui::run,
13 Id, Result,
14};
15
16#[derive(Parser)]
18pub struct Args {
19 #[command(subcommand)]
20 command: Option<Commands>,
21}
22
23#[derive(Subcommand)]
25enum Commands {
26 Clean,
28 List(ListArgs),
30 Add(AddArgs),
32 Remove(RemoveArgs),
34 Edit(EditArgs),
36 Mark(MarkArgs),
38}
39
40#[derive(ClapArgs)]
41struct ListArgs {
42 #[arg(short)]
44 completed: Option<bool>,
45 #[arg(short = 'p')]
47 incomplete: Option<bool>,
48 #[arg(short)]
50 id: Option<Id>,
51 #[arg(short = 'f')]
53 fuzzy: Option<String>,
54}
55
56#[derive(ClapArgs)]
57struct AddArgs {
58 #[arg(short)]
60 description: String,
61}
62
63#[derive(ClapArgs)]
64struct RemoveArgs {
65 #[arg(short)]
67 id: Id,
68}
69
70#[derive(ClapArgs)]
71struct EditArgs {
72 #[arg(short)]
73 id: Id,
74 #[arg(short)]
75 description: String,
76}
77
78#[derive(ClapArgs)]
79struct MarkArgs {
80 #[arg(short)]
81 id: Id,
82}
83
84fn show_multiple_tasks_in_a_table(data: State, options: &ListArgs) -> Result<()> {
85 let mut table = Vec::new();
86 for task in data.get_tasks() {
87 if (options.completed.is_some() && !task.completed)
88 || (options.incomplete.is_some() && task.completed)
89 {
90 continue;
91 }
92 if let Some(search) = &options.fuzzy {
94 if best_match(search, &task.desc).is_none() {
95 continue;
96 }
97 }
98 table.push(vec![
99 task.id.cell(),
100 task.desc.clone().cell(),
101 match task.completed {
102 true => "Completed".cell().foreground_color(Some(Color::Green)),
103 false => "Pending"
104 .cell()
105 .bold(true)
106 .foreground_color(Some(Color::Red)),
107 },
108 format_date(task.last_updated).cell(),
109 ]);
110 }
111 let table = table.table().title(vec![
112 "Task ID"
113 .cell()
114 .bold(true)
115 .foreground_color(Some(Color::Blue)),
116 "Task Description"
117 .cell()
118 .bold(true)
119 .foreground_color(Some(Color::Blue)),
120 "Status"
121 .cell()
122 .bold(true)
123 .foreground_color(Some(Color::Blue)),
124 "Last Updated"
125 .cell()
126 .bold(true)
127 .foreground_color(Some(Color::Blue)),
128 ]);
129 print_stdout(table)?;
130 Ok(())
131}
132
133impl Args {
134 pub fn run(&self) -> Result<()> {
136 if let Some(command) = &self.command {
137 match command {
138 Commands::Clean => {
139 println!("Are you sure you want to delete all tasks?(y/n)");
140 let mut ans = String::new();
141 std::io::stdin().read_line(&mut ans)?;
142 let ans = ans.trim();
143 if ans.eq("y") {
144 remove_metadata()?;
145 println!("All tasks removed successfuly");
146 }
147 }
148 Commands::List(options) => {
149 if check_existing_metadata() {
150 let data = read_data_from_file()?;
151 if data.tasks.is_empty() {
152 println!("No tasks yet!");
153 } else {
154 if let Some(id) = options.id {
156 let task = data.tasks.get(&id);
157 if let Some(list_item) = task {
158 println!(
159 "TASK FOUND\nDescription: {}\nStatus: {}",
160 list_item.task.desc,
161 match list_item.task.completed {
162 true => "Completed",
163 false => "Pending",
164 }
165 );
166 } else {
167 println!("No such task found!");
168 }
169 } else {
170 show_multiple_tasks_in_a_table(data, options)?;
171 }
172 }
173 } else {
174 println!("No tasks yet");
175 }
176 }
177 Commands::Add(add_args) => {
178 if !check_existing_metadata() {
179 create_metadata()?;
180 }
181 let mut data = read_data_from_file()?;
182 data.add_task(&add_args.description);
183 enter_data_to_file(&data)?;
184 println!("Added new task successfully");
185 }
186 Commands::Remove(remove_args) => {
187 if check_existing_metadata() {
188 let mut data = read_data_from_file()?;
189 if data.remove_task(&remove_args.id).is_none() {
190 println!("No such task found");
191 } else {
192 enter_data_to_file(&data)?;
193 }
194 }
195 }
196 Commands::Edit(edit_args) => {
197 if check_existing_metadata() {
198 let mut data = read_data_from_file()?;
199 if data.remove_task(&edit_args.id).is_some() {
200 data.add_task(&edit_args.description);
201 enter_data_to_file(&data)?;
202 println!("Task changed successfully");
203 } else {
204 println!("No task with this id found");
205 }
206 }
207 }
208 Commands::Mark(mark_args) => {
209 if check_existing_metadata() {
210 let mut data = read_data_from_file()?;
211 if let Some(complete) = data.toggle_task_status_by_id(mark_args.id) {
212 enter_data_to_file(&data)?;
213 if complete {
214 println!("Marked task as complete");
215 } else {
216 println!("Marked task as incomplete");
217 }
218 } else {
219 println!("No such task found");
220 }
221 }
222 }
223 }
224 } else {
225 run()?;
226 }
227 Ok(())
228 }
229}