cli_todo_list_sqlite/
lib.rs

1use colored::Colorize;
2use rusqlite::Connection;
3use rusqlite::Result;
4use rusqlite::Statement;
5use std::error::Error;
6use std::fmt;
7
8#[derive(Debug)]
9struct MyError(String);
10
11impl fmt::Display for MyError {
12    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
13        write!(f, "{}", self.0)
14    }
15}
16
17impl Error for MyError {}
18
19pub struct Config {
20    pub command: String,
21    pub content: String,
22}
23
24impl Config {
25    pub fn config_error() -> Box<dyn Error> {
26        let error_msg = "\tUsage:
27    1. add <sentence> for adding a task in todo-list
28    2. list for listing the tasks in the todo-list
29    3. remove <id_task> for removing a task in the todo-list
30    4. done <id_task> for marking a task as done
31    5. reset for reseting all the tasks in todo-list
32    6. sort -asc for sorting tasks ascendent , -desc for sorting tasks descendent by the name
33    7. rmn for listing all the remaing tasks that are not done";
34        Box::new(MyError(error_msg.into()))
35    }
36    pub fn build(args: &[String]) -> Result<Config, Box<dyn Error>> {
37        if args.len() < 2 {
38            Err(Self::config_error())
39        } else {
40            let command1 = args[1].clone();
41            //let content1 = args[2].clone();
42            let mut content1: String = String::new();
43            if args.len() >= 2 {
44                for arg in args.iter().skip(2) {
45                    content1.push_str(arg);
46                    content1.push(' ');
47                }
48            }
49            Ok(Config {
50                command: command1,
51                content: content1,
52            })
53        }
54    }
55    pub fn build_database(path: &str) -> Result<Connection, Box<dyn Error>> {
56        let conn = Connection::open(path)?;
57        conn.execute(
58            "CREATE TABLE IF NOT EXISTS todo_list(
59            id  INTEGER PRIMARY KEY,
60            name TEXT NOT NULL,
61            status BOOL
62            )",
63            (),
64        )?;
65        Ok(conn)
66    }
67}
68
69pub struct Task {
70    id: i32,
71    name: String,
72    status: bool,
73}
74
75impl Task {
76    /*fn new(name1: String) -> Task {
77        Task {
78            id: None,
79            name: name1,
80            status: false,
81        }
82    }*/
83    pub fn add(name: String, conn: &Connection) -> Result<(), Box<dyn Error>> {
84        let id_m: Option<i32> = conn
85            .query_row("SELECT MAX(id) FROM todo_list", [], |row| row.get(0))
86            .unwrap_or(None);
87        let new_id = id_m.unwrap_or(0) + 1;
88        conn.execute(
89            "INSERT INTO todo_list (id, name, status) VALUES (?1, ?2, ?3)",
90            (new_id, &name, false),
91        )?;
92
93        Ok(())
94    }
95    pub fn make_string(id: &i32, name: &str, status: &bool) -> String {
96        let mut temp = String::new();
97        //let number: i32 = id.parse().unwrap("salut");
98        temp.push_str(&id.to_string());
99        temp.push('.');
100        temp.push_str(name);
101        temp.push_str(" : ");
102        if *status {
103            temp.push_str("done");
104        } else {
105            temp.push_str("not done");
106        }
107        temp
108    }
109    pub fn fetch_tasks(stmt: &mut Statement) -> Result<(), Box<dyn Error>> {
110        let task_iter = stmt.query_map([], |row| {
111            Ok(Task {
112                id: row.get(0)?,
113                name: row.get(1)?,
114                status: row.get(2)?,
115            })
116        })?;
117        for task in task_iter {
118            let temp = task?;
119            let output = Self::make_string(&temp.id, &temp.name, &temp.status);
120            if temp.status {
121                println!("{}", output.green());
122            } else {
123                println!("{}", output.red());
124            }
125        }
126        Ok(())
127    }
128
129    pub fn list(conn: &Connection) -> Result<(), Box<dyn Error>> {
130        let mut stmt = conn.prepare("SELECT id, name, status FROM todo_list")?;
131        Self::fetch_tasks(&mut stmt)?;
132        Ok(())
133    }
134
135    fn search_id(conn: &Connection, id: i32) -> Result<(), Box<dyn Error>> {
136        let mut stmt = conn.prepare("SELECT id FROM todo_list")?;
137        let id_iter = stmt.query_map([], |row| row.get::<_, i32>(0))?;
138        for ids in id_iter {
139            let temp = ids?;
140            if temp == id {
141                return Ok(());
142            }
143        }
144
145        Err(Box::new(MyError("the id is not in the todo_list!".into())))
146    }
147
148    pub fn remove(conn: &Connection, id: i32) -> Result<(), Box<dyn Error>> {
149        Self::search_id(conn, id)?;
150        conn.execute("DELETE from todo_list WHERE id = ?1", (id,))?;
151        Ok(())
152    }
153
154    pub fn mark_as_done(conn: &Connection, id: i32) -> Result<(), Box<dyn Error>> {
155        Self::search_id(conn, id)?;
156        conn.execute("UPDATE todo_list SET status = true WHERE id = ?1", (id,))?;
157        Ok(())
158    }
159
160    pub fn reset(conn: &Connection) -> Result<(), Box<dyn Error>> {
161        conn.execute("DELETE FROM todo_list", ())?;
162        Ok(())
163    }
164
165    pub fn sort(conn: &Connection, temp: &str) -> Result<(), Box<dyn Error>> {
166        let mut stmt;
167        if temp == "-asc" {
168            stmt = conn.prepare("SELECT id, name, status FROM todo_list ORDER BY name ASC")?;
169        } else if temp == "-desc" {
170            stmt = conn.prepare("SELECT id, name, status FROM todo_list ORDER BY name DESC")?;
171        } else {
172            return Err(Box::new(MyError("cannot be sorted!".into())));
173        }
174        Self::fetch_tasks(&mut stmt)?;
175
176        Ok(())
177    }
178    pub fn rmn(conn: &Connection) -> Result<(), Box<dyn Error>> {
179        let mut stmt =
180            conn.prepare("SELECT id, name, status FROM todo_list WHERE status = false")?;
181        Self::fetch_tasks(&mut stmt)?;
182        Ok(())
183    }
184}
185
186// IN function run i define the connection to the database
187pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
188    let conn = Config::build_database("./my_db.db3")?;
189    //println!("A intrat si aici!");
190    match config.command.as_str() {
191        "add" => {
192            Task::add(config.content, &conn)?;
193        }
194        "list" => {
195            Task::list(&conn)?;
196        }
197        "remove" => {
198            let id: i32 = config.content.trim().parse()?;
199            Task::remove(&conn, id)?;
200        }
201        //"edit" =>
202        "done" => {
203            let id: i32 = config.content.trim().parse()?;
204            Task::mark_as_done(&conn, id)?;
205        }
206        "reset" => {
207            Task::reset(&conn)?;
208        }
209        "sort" => {
210            let temp = config.content.trim();
211            Task::sort(&conn, temp)?;
212        }
213        "rmn" => {
214            Task::rmn(&conn)?;
215        }
216        _ => {
217            return Err(Config::config_error());
218        }
219    }
220    Ok(())
221}