cli_todo_list_sqlite/
lib.rs1use 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 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 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 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
186pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
188 let conn = Config::build_database("./my_db.db3")?;
189 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 "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}