1use std::fmt::Error;
2
3use chrono::{DateTime, Utc};
4use rusqlite::{Connection, params, Result, Row};
5use rusqlite::types::{FromSql, ValueRef};
6use serde::{Deserialize, Serialize};
7use termion::{color, style};
8use uuid::Uuid;
9
10use db::DatabaseOperations;
11
12use crate::modules::db;
13use crate::modules::db::{get_connection, serializers};
14use crate::modules::db::serializers::SerializableDateTime;
15use crate::modules::tasks::todoist::core::Task as TodoisTask;
16
17#[derive(Serialize, Deserialize)]
18pub struct Project {
19 pub id: Uuid,
20 pub name: String,
21 pub description: String,
22 pub created_at: SerializableDateTime,
23 pub updated_at: SerializableDateTime,
24}
25
26impl DatabaseOperations<String> for Project {
27 fn save(&self, db: &str) -> Result<(), Error> {
29 let conn = Connection::open(db).ok().unwrap();
30 let id = Uuid::new_v4();
31 let now = Utc::now().to_string();
32
33 conn.execute(
34 "INSERT INTO project (id, name, description, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5)",
35 params![id.to_string(), self.name, self.description, now, now],
36 ).ok().unwrap();
37
38 Ok(())
39 }
40
41 fn get(id: String, db: &str) -> Result<Self, rusqlite::Error> where Self: Sized {
42 todo!()
43 }
44
45 fn get_all(db: &str) -> Result<Vec<Project>, rusqlite::Error> {
47 let conn = Connection::open(db).ok().unwrap();
48 let mut stmt = conn.prepare("SELECT * FROM project").ok().unwrap();
49 let rows = stmt
50 .query_map((), |row| {
51 let id: Vec<u8> = row.get(0).ok().unwrap();
52 Ok(Project {
53 id: Uuid::from_slice(&id).unwrap(),
54 name: row.get(1).ok().unwrap(),
55 description: row.get(2).ok().unwrap(),
56 created_at: row.get(3).ok().unwrap(),
57 updated_at: row.get(4).ok().unwrap(),
58 })
59 })
60 .ok()
61 .unwrap();
62
63 let mut projects = Vec::new();
64 for project in rows {
65 projects.push(project.ok().unwrap());
66 }
67
68 Ok(projects)
69 }
70
71 fn map(row: &Row<'_>) -> std::result::Result<Self, rusqlite::Error> where Self: Sized {
72 todo!()
73 }
74}
75
76impl Default for Project {
77 fn default() -> Self {
78 let now = SerializableDateTime::now();
79 Self {
80 id: Uuid::new_v4(),
81 name: "Default name".to_string(),
82 description: "Default description".to_string(),
83 created_at: now.clone(),
84 updated_at: now.clone(),
85 }
86 }
87}
88
89#[derive(Debug, Serialize, Deserialize)]
90pub struct Sprint {
91 pub id: Uuid,
92 pub project_id: Uuid,
93 pub name: String,
94 pub start_date: SerializableDateTime,
95 pub end_date: SerializableDateTime,
96 pub created_at: SerializableDateTime,
97 pub updated_at: SerializableDateTime,
98}
99
100impl DatabaseOperations<String> for Sprint {
101 fn save(&self, db: &str) -> Result<(), Error> {
103 let conn = Connection::open(db).ok().unwrap();
104
105 let now = Utc::now().to_rfc3339().to_string();
106
107 conn.execute(
108 "INSERT INTO sprint (id, project_id, name, start_date, end_date, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
109 params![self.id.to_string(), &self.project_id.to_string(), &self.name, &self.start_date.with_timezone(&Utc).to_string(), &self.end_date.with_timezone(&Utc).to_string(), now, now],
110 ).ok().unwrap();
111
112 Ok(())
113 }
114
115 fn get(id: String, db: &str) -> Result<Self, rusqlite::Error> where Self: Sized {
116 let conn = get_connection(db);
117 let mut query = conn.prepare("SELECT * FROM sprint WHERE id = ?")?;
118
119 query.query_row(params![&id], Sprint::map)
120 }
121
122 fn get_all(db: &str) -> Result<Vec<Sprint>, rusqlite::Error> {
124 let conn = Connection::open(db)?;
125 let mut stmt = conn.prepare("SELECT * FROM sprint")?;
126 let rows = stmt
127 .query_map((), Sprint::map)?;
128
129 let mut sprints = Vec::new();
130 for sprint_res in rows {
131 sprints.push(sprint_res?);
132 }
133
134 Ok(sprints)
135 }
136 fn map(row: &Row<'_>) -> Result<Self, rusqlite::Error> where Self: Sized {
137 let id: String = row.get(0)?;
138 let project_id: String = row.get(1)?;
139 let start_date: String = row.get(3)?;
140 let end_date: String = row.get(4)?;
141 let created_at: String = row.get(5)?;
142 let updated_at: String = row.get(6)?;
143 Ok(Sprint {
144 id: Uuid::parse_str(&id).unwrap(),
145 project_id: Uuid::parse_str(&project_id).unwrap(),
146 name: row.get(2)?,
147 start_date: SerializableDateTime::parse_from_rfc3339(&start_date)
148 .unwrap()
149 .with_timezone(&Utc),
150 end_date: SerializableDateTime::parse_from_rfc3339(&end_date)
151 .unwrap()
152 .with_timezone(&Utc),
153 created_at: SerializableDateTime::parse_from_rfc3339(&created_at)
154 .unwrap()
155 .with_timezone(&Utc),
156 updated_at: SerializableDateTime::parse_from_rfc3339(&updated_at)
157 .unwrap()
158 .with_timezone(&Utc),
159 })
160 }
161}
162
163pub fn sprint_get_all(db: &str) -> Result<Vec<Sprint>, rusqlite::Error> {
164 Sprint::get_all(db)
165}
166
167impl Default for Sprint {
168 fn default() -> Self {
169 let now = SerializableDateTime::now();
170 Self {
171 id: Uuid::new_v4(),
172 project_id: Uuid::new_v4(),
173 name: "Default name".to_string(),
174 start_date: now.clone(),
175 end_date: now.add_weeks(3),
176 created_at: now.clone(),
177 updated_at: now.clone(),
178 }
179 }
180}
181
182impl Sprint {
183 fn is_active(&self) -> bool {
184 let now = Utc::now();
185 self.start_date.get_utc() <= now && now <= self.end_date.get_utc()
186 }
187}
188
189pub fn delete_project_by_id(conn: &Connection, id: Uuid) -> Result<()> {
191 conn.execute("DELETE FROM project WHERE id = ?1", params![id.to_string()])?;
192
193 Ok(())
194}
195
196pub fn delete_project_by_name(conn: &Connection, name: &str) -> Result<()> {
198 conn.execute("DELETE FROM project WHERE name = ?1", params![name])?;
199
200 Ok(())
201}
202
203pub fn create_sprint(
205 conn: &Connection,
206 project_id: Uuid,
207 name: &str,
208 start_date: DateTime<Utc>,
209 end_date: DateTime<Utc>,
210) -> Result<()> {
211 let id = Uuid::new_v4();
212 let now = Utc::now().to_string();
213
214 conn.execute(
215 "INSERT INTO sprint (id, project_id, name, start_date, end_date, created_at, updated_at) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
216 params![id.to_string(), project_id.to_string(), name, start_date.to_string(), end_date.to_string(), now, now],
217 )?;
218
219 Ok(())
220}
221
222pub fn delete_sprint(conn: &Connection, id: Uuid) -> Result<()> {
224 conn.execute("DELETE FROM sprint WHERE id = ?1", params![id.to_string()])?;
225
226 Ok(())
227}
228
229pub fn list_sprints_for_project(conn: &Connection, project_id: Uuid) -> Result<Vec<Sprint>> {
231 let mut stmt = conn.prepare("SELECT * FROM sprint WHERE project_id = ?1")?;
232 let rows = stmt.query_map(params![project_id.to_string()], Sprint::map)?;
233
234 let mut sprints = Vec::new();
235 for sprint_res in rows {
236 sprints.push(sprint_res?);
237 }
238
239 Ok(sprints)
240}
241
242pub fn print_sprint_info(db: &str, sprint_id: Uuid) -> Result<()> {
243 let conn = get_connection(db);
244 let sprint_info = Sprint::get(sprint_id.to_string(), db).ok().unwrap();
245
246 println!("\n{}{}Sprint Information{}", style::Bold, color::Fg(color::Blue), style::Reset);
247 println!("{}ID:{} {}", color::Fg(color::Green), style::Reset, sprint_info.id.to_string());
248 println!("{}Name:{} {}", color::Fg(color::Green), style::Reset, sprint_info.name);
249 println!("{}Start Date:{} {}", color::Fg(color::Green), style::Reset, sprint_info.start_date.get_utc().to_string());
250 println!("{}End Date:{} {}", color::Fg(color::Green), style::Reset, sprint_info.end_date.get_utc().to_string());
251
252 let now = Utc::now();
253 let days_to_finish = sprint_info.end_date.get_utc().signed_duration_since(now).num_days();
254 println!("{}Days to Sprint Finish:{} {}", color::Fg(color::Green), style::Reset, days_to_finish.to_string());
255
256 let mut task_query = conn.prepare("
257 SELECT task.* FROM task
258 INNER JOIN sprint_todoist_task ON task.id = sprint_todoist_task.todoist_task_id
259 WHERE sprint_todoist_task.sprint_id = ?
260 ")?;
261 let task_rows = task_query.query_map(params![sprint_id.to_string()], |row| {
262 Ok(TodoisTask {
263 id: row.get(0)?,
264 content: row.get(1)?,
265 labels: vec![],
267 })
268 })?;
269
270 let tasks: Result<Vec<TodoisTask>, _> = task_rows.collect();
271 match tasks {
272 Ok(tasks) => {
273 println!("\n{}{}Tasks{}", style::Bold, color::Fg(color::Blue), style::Reset);
274 for task in tasks {
275 println!("{}Task ID:{} {}", color::Fg(color::Green), style::Reset, task.id);
276 println!("{}Content:{} {}", color::Fg(color::Green), style::Reset, task.content.unwrap_or_else(|| "None".to_string()));
277 println!("{}Labels:{} {:?}", color::Fg(color::Green), style::Reset, task.labels);
278 }
279 }
280 Err(e) => println!("Failed to get tasks: {}", e),
281 }
282
283 Ok(())
284}