todo_cli_manikya/
args.rs

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/// Args to be used for the application
17#[derive(Parser)]
18pub struct Args {
19    #[command(subcommand)]
20    command: Option<Commands>,
21}
22
23/// All the available commands
24#[derive(Subcommand)]
25enum Commands {
26    /// Remove all the existing tasks from the database
27    Clean,
28    /// List out all the existing tasks
29    List(ListArgs),
30    /// Add a new task
31    Add(AddArgs),
32    /// Remove a task
33    Remove(RemoveArgs),
34    /// Edit a task with an id
35    Edit(EditArgs),
36    /// Mark a task complete or incomplete
37    Mark(MarkArgs),
38}
39
40#[derive(ClapArgs)]
41struct ListArgs {
42    /// List only the tasks which are completed
43    #[arg(short)]
44    completed: Option<bool>,
45    /// List only the tasks which are incomplete
46    #[arg(short = 'p')]
47    incomplete: Option<bool>,
48    /// Show a task with particular id
49    #[arg(short)]
50    id: Option<Id>,
51    /// Get the required tasks using a fuzzy search
52    #[arg(short = 'f')]
53    fuzzy: Option<String>,
54}
55
56#[derive(ClapArgs)]
57struct AddArgs {
58    /// Description for the command to be added
59    #[arg(short)]
60    description: String,
61}
62
63#[derive(ClapArgs)]
64struct RemoveArgs {
65    /// Id of the task to be removed
66    #[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        // fuzzy search
93        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    /// The main function which runs the entire app
135    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                            // only want a certain task
155                            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}