1use crate::{
2 datetime_str_is_past, display_message, get_connection, get_tasks, select_board,
3 user_input::{get_user_confirmation, get_user_date, get_user_input, get_user_selection},
4 Board, Color, Comment, Record, Task, DATETIME_FORMAT, SAMPLE_TITLE, TABLE_BOARDS,
5 TABLE_COMMENTS, TABLE_TASKS,
6};
7use chrono::Utc;
8use rusqlite::{params, Result};
9use tabled::{Disable, Style, Table};
10
11pub fn get_records_qtd(table: &str) -> Result<u16> {
12 let conn = get_connection();
13 let query = format!("SELECT COUNT(*) FROM {table}");
14 let mut stmt = conn.prepare(&query)?;
15 let query_result = stmt.query_map([], |row| Ok(Record { qtd: row.get(0)? }))?;
16 let qtd_records = query_result.last().unwrap()?.qtd;
17 Ok(qtd_records)
18}
19
20pub fn create_database() -> Result<()> {
22 let conn = get_connection();
23
24 conn.execute(
26 &format!(
27 "CREATE TABLE IF NOT EXISTS {TABLE_BOARDS} (
28 id INTEGER PRIMARY KEY,
29 title VARCHAR(255) NOT NULL
30 );"
31 ),
32 [],
33 )?;
34
35 conn.execute(
37 &format!(
38 "CREATE TABLE IF NOT EXISTS {TABLE_TASKS} (
39 id INTEGER PRIMARY KEY,
40 title VARCHAR(255) NOT NULL,
41 done INTEGER NOT NULL,
42 board_id INTEGER NOT NULL,
43 created_at VARCHAR(255) NOT NULL,
44 reminder VARCHAR(255) NOT NULL,
45 FOREIGN KEY(board_id) REFERENCES {TABLE_BOARDS}(id)
46 );"
47 ),
48 [],
49 )?;
50
51 conn.execute(
53 &format!(
54 "CREATE TABLE IF NOT EXISTS {TABLE_COMMENTS} (
55 id INTEGER PRIMARY KEY,
56 title VARCHAR(255) NOT NULL,
57 task_id INTEGER NOT NULL,
58 created_at VARCHAR(255) NOT NULL,
59 FOREIGN KEY(task_id) REFERENCES {TABLE_TASKS}(id)
60 );"
61 ),
62 [],
63 )?;
64
65 Ok(())
66}
67
68pub fn edit_board(title: &str, id: u16) -> Result<()> {
69 let conn = get_connection();
70 let title = get_user_input("New Board title", title, true).unwrap();
71
72 conn.execute(
73 &format!("UPDATE {TABLE_BOARDS} SET title = ?1 WHERE id = ?2"),
74 params![title, id],
75 )
76 .unwrap();
77 Ok(())
78}
79
80pub fn get_comments_by_task_id(task_id: u16) -> Result<Vec<Comment>> {
81 let conn = get_connection();
82 let query = format!("SELECT * FROM {TABLE_COMMENTS} WHERE task_id = {task_id}");
83 let mut comments: Vec<Comment> = Vec::new();
84
85 let mut stmt = conn.prepare(&query)?;
86
87 let result_iter = stmt.query_map([], |row| {
88 Ok(Comment {
89 id: row.get(0)?,
90 title: row.get(1)?,
91 created_at: row.get(3)?,
92 })
93 })?;
94
95 for i in result_iter {
96 comments.push(i?);
97 }
98 Ok(comments)
99}
100
101pub fn select_task(done: u8) -> Option<(String, u16)> {
103 let records_qtd = get_records_qtd(TABLE_TASKS).unwrap();
104 if records_qtd == 0 {
105 display_message("info", "No Tasks found in database", Color::Cyan);
106 return None;
107 }
108 let mut query = format!("SELECT * FROM {TABLE_TASKS} WHERE done = {done}");
109
110 let all_boards = get_user_confirmation("From all boards");
111
112 if !all_boards {
113 let (_, board_id) = select_board().unwrap();
114 query.push_str(&format!(" AND board_id = {board_id}"));
115 }
116
117 let tasks_raw = get_tasks(&query).unwrap();
118 if tasks_raw.len() == 0 {
119 display_message(
120 "info",
121 "No Tasks found in database with these criteria",
122 Color::Cyan,
123 );
124 return None;
125 }
126
127 let mut tasks: Vec<String> = tasks_raw
128 .iter()
129 .map(|x| format!("{} - {}", &x.id, &x.title))
130 .collect();
131
132 tasks.sort();
133
134 let (selected_task, _) = get_user_selection(&tasks, "Task");
135
136 let task_id: u16 = selected_task
137 .split_whitespace()
138 .next()
139 .unwrap()
140 .parse()
141 .unwrap();
142
143 let selected_task = tasks_raw.iter().find(|x| x.id == task_id).unwrap();
144 let selected_task = (selected_task.title.to_string(), selected_task.id);
145 Some(selected_task)
146}
147
148pub fn list_delayed_tasks() {
149 let query = format!("SELECT * FROM {TABLE_TASKS} WHERE done = 0");
150 let tasks_raw = get_tasks(&query).unwrap();
151
152 if tasks_raw.len() == 0 {
153 return;
154 }
155
156 let delayed_tasks: Vec<&Task> = tasks_raw
157 .iter()
158 .filter(|x| datetime_str_is_past(&x.reminder))
159 .collect();
160
161 if delayed_tasks.len() == 0 {
162 return;
163 }
164 println!("Delayed Tasks: {}", &delayed_tasks.len());
165
166 let table = Table::new(delayed_tasks)
167 .with(Style::modern())
168 .with(Disable::Column(2..4));
169
170 println!("{}", table);
171}
172
173pub fn delete_record_by_id(table: &str, id: u16) -> Result<()> {
175 let conn = get_connection();
176 conn.execute(&format!("DELETE FROM {table} WHERE id = ?1"), params![id])?;
177 Ok(())
178}
179
180pub fn get_boards() -> Result<Vec<Board>> {
181 let conn = get_connection();
182 let query = format!("SELECT * FROM {TABLE_BOARDS}");
183 let mut records: Vec<Board> = Vec::new();
184
185 let mut stmt = conn.prepare(&query)?;
186
187 let result_iter = stmt.query_map([], |row| {
188 Ok(Board {
189 id: row.get(0)?,
190 title: row.get(1)?,
191 })
192 })?;
193
194 for i in result_iter {
195 records.push(i?);
196 }
197 Ok(records)
198}
199
200pub fn create_comment(task_id: u16) -> Result<()> {
201 let conn = get_connection();
202
203 let title = get_user_input("Comment title", SAMPLE_TITLE, true).unwrap();
204 let created_at = Utc::now().to_rfc2822();
205
206 conn.execute(
207 &format!("INSERT INTO {TABLE_COMMENTS} (title, task_id, created_at) VALUES (?1, ?2, ?3)"),
208 params![title, task_id, created_at],
209 )?;
210 Ok(())
211}
212
213pub fn set_reminder(task_id: u16) -> Result<()> {
214 let conn = get_connection();
215
216 let reminder = get_user_date(true, false);
217 if reminder.is_none() {
218 return Ok(());
219 }
220
221 conn.execute(
222 &format!("UPDATE {TABLE_TASKS} SET reminder = ?1 WHERE id = ?2"),
223 params![reminder, task_id],
224 )?;
225 Ok(())
226}
227
228pub fn create_task() -> Result<()> {
229 let conn = get_connection();
230 let boards_qtd = get_records_qtd(TABLE_BOARDS).unwrap();
231
232 if boards_qtd == 0 {
233 display_message("info", "Create a initial Board", Color::Cyan);
234 create_board().unwrap();
235 }
236
237 let title = get_user_input("Task title", SAMPLE_TITLE, true).unwrap();
238 let board_id = select_board().unwrap().1;
239 let created_at = Utc::now().format(DATETIME_FORMAT).to_string();
240 let with_reminder = get_user_confirmation("Set reminder");
241
242 let reminder = match with_reminder {
243 true => get_user_date(true, true).unwrap(),
244 false => "".to_string(),
245 };
246
247 conn.execute(
248 &format!("INSERT INTO {TABLE_TASKS} (title, done, board_id, created_at, reminder) VALUES (?1, ?2, ?3, ?4, ?5)"),
249 params![title, 0, board_id, created_at, reminder],
250 )
251 .unwrap();
252 Ok(())
253}
254
255pub fn create_board() -> Result<()> {
256 let conn = get_connection();
257 let title = get_user_input("Board title", SAMPLE_TITLE, true).unwrap();
258 let board = Board { id: 0, title };
259
260 conn.execute(
261 &format!("INSERT INTO {TABLE_BOARDS} (title) VALUES (?1)"),
262 params![board.title],
263 )
264 .unwrap();
265 Ok(())
266}
267
268pub fn switch_task_status(task_id: u16) -> Result<()> {
269 let conn = get_connection();
270
271 let done: u8 = match get_user_confirmation("Done") {
272 true => 1,
273 false => 0,
274 };
275
276 conn.execute(
277 &format!("UPDATE {TABLE_TASKS} SET done = ?1 WHERE id = ?2"),
278 params![done, task_id],
279 )
280 .unwrap();
281 Ok(())
282}