saturn_cli/db/
memory.rs

1use super::unixfile::UnixFileLoader;
2use crate::{
3    db::DB,
4    filenames::saturn_db,
5    record::{Record, RecurringRecord},
6    time::now,
7};
8use anyhow::{anyhow, Result};
9use async_trait::async_trait;
10use chrono::Timelike;
11use serde::{Deserialize, Serialize};
12use std::collections::BTreeMap;
13
14#[derive(Debug, Clone, Serialize, Deserialize, Default)]
15pub struct MemoryDB {
16    primary_key: u64,
17    records: BTreeMap<u64, Record>,
18    recurrence_key: u64,
19    recurring: BTreeMap<u64, RecurringRecord>,
20}
21
22impl MemoryDB {
23    pub fn new() -> Self {
24        Self::default()
25    }
26}
27
28#[async_trait]
29impl DB for MemoryDB {
30    async fn load(&mut self) -> Result<()> {
31        let db: Self = UnixFileLoader::new(&saturn_db()).load().await?;
32        self.primary_key = db.primary_key;
33        self.records = db.records;
34        self.recurrence_key = db.recurrence_key;
35        self.recurring = db.recurring;
36        Ok(())
37    }
38
39    async fn dump(&self) -> Result<()> {
40        UnixFileLoader::new(&saturn_db()).dump(self.clone()).await
41    }
42
43    fn primary_key(&self) -> u64 {
44        self.primary_key
45    }
46
47    fn recurrence_key(&self) -> u64 {
48        self.recurrence_key
49    }
50
51    fn set_primary_key(&mut self, primary_key: u64) {
52        self.primary_key = primary_key;
53    }
54
55    fn set_recurrence_key(&mut self, primary_key: u64) {
56        self.recurrence_key = primary_key;
57    }
58
59    async fn delete(&mut self, primary_key: u64) -> Result<()> {
60        self.records.remove(&primary_key);
61        Ok(())
62    }
63
64    async fn delete_recurrence(&mut self, recurrence_key: u64) -> Result<Vec<String>> {
65        self.recurring.remove(&recurrence_key);
66        Ok(Vec::new()) // FIXME NFI why this is being returned
67    }
68
69    async fn record(&mut self, record: Record) -> Result<()> {
70        self.records.insert(record.primary_key(), record);
71        Ok(())
72    }
73
74    async fn record_recurrence(&mut self, record: RecurringRecord) -> Result<()> {
75        self.recurring.insert(record.recurrence_key(), record);
76        Ok(())
77    }
78
79    async fn insert_record(&mut self, record: Record) -> Result<()> {
80        self.record(record).await
81    }
82
83    async fn insert_recurrence(&mut self, record: RecurringRecord) -> Result<()> {
84        self.record_recurrence(record).await
85    }
86
87    async fn list_recurrence(&mut self) -> Result<Vec<RecurringRecord>> {
88        let mut v = Vec::new();
89
90        for (_, val) in &self.recurring {
91            v.push(val.clone());
92        }
93
94        Ok(v)
95    }
96
97    async fn update_recurrence(&mut self) -> Result<()> {
98        let mut recurring = self.recurring.clone();
99        let records = self.records.clone();
100
101        for (_, recur) in &mut recurring {
102            let mut seen: Option<&Record> = None;
103
104            let mut begin = recur.record().datetime();
105            let tomorrow = (now() + chrono::TimeDelta::try_days(1).unwrap_or_default()).date_naive();
106
107            while begin.date_naive() <= tomorrow {
108                for (_, record) in &records {
109                    if let Some(key) = record.recurrence_key() {
110                        if key == recur.recurrence_key() && record.datetime() == begin {
111                            seen = Some(record);
112                        }
113                    }
114                }
115
116                if seen.is_none() {
117                    let key = self.next_key();
118                    self.record(recur.record_from(key, begin.naive_local()))
119                        .await?;
120                }
121
122                begin += recur.recurrence().duration();
123            }
124        }
125
126        Ok(())
127    }
128
129    async fn list_today(&mut self, include_completed: bool) -> Result<Vec<Record>> {
130        let today = now().date_naive();
131
132        Ok(self
133            .records
134            .iter()
135            .filter_map(|(_, v)| {
136                if v.date() != today || (v.completed() && !include_completed) {
137                    None
138                } else {
139                    Some(v.clone())
140                }
141            })
142            .collect::<Vec<Record>>())
143    }
144
145    async fn list_all(&mut self, include_completed: bool) -> Result<Vec<Record>> {
146        let values = self
147            .records
148            .iter()
149            .filter(|(_, v)| {
150                if v.completed() && !include_completed {
151                    false
152                } else {
153                    true
154                }
155            })
156            .collect::<BTreeMap<&u64, &Record>>();
157
158        let mut v = Vec::new();
159
160        for (_, val) in values {
161            v.push(val.clone())
162        }
163
164        Ok(v)
165    }
166
167    async fn events_now(
168        &mut self,
169        last: chrono::Duration,
170        include_completed: bool,
171    ) -> Result<Vec<Record>> {
172        let mut ret = Vec::new();
173        let n = now().date_naive();
174
175        let mut records = Vec::new();
176
177        for record in self.records.iter().filter(|(_, v)| v.date() == n) {
178            records.push(record);
179        }
180
181        let n = n + chrono::TimeDelta::try_days(1).unwrap_or_default();
182
183        let mut next_day = self.records.iter().filter(|(_, v)| v.date() == n).collect();
184
185        records.append(&mut next_day);
186
187        for (_, item) in records {
188            if item.completed() && !include_completed {
189                continue;
190            }
191
192            if let Some(at) = item.at() {
193                if at - now().time() < last && now().time() < at {
194                    ret.push(item.clone());
195                }
196            } else if let Some(schedule) = item.scheduled() {
197                if (schedule.0 - last) < now().time() && (schedule.1 + last) > now().time() {
198                    ret.push(item.clone())
199                }
200            } else if item.all_day()
201                && item.date() - chrono::TimeDelta::try_days(1).unwrap_or_default() == now().date_naive()
202                && now().time() > chrono::NaiveTime::from_hms_opt(23, 59, 0).unwrap() - last
203            {
204                ret.push(item.clone())
205            } else {
206                let dt = item.datetime();
207                let n = now();
208                if dt > n && n > dt - last {
209                    ret.push(item.clone());
210                } else if let Some(notifications) = item.notifications() {
211                    for notification in notifications {
212                        let dt_window = dt - notification.duration();
213                        let dt_time = dt_window
214                            .time()
215                            .with_second(0)
216                            .unwrap()
217                            .with_nanosecond(0)
218                            .unwrap();
219                        let n_time = n.time().with_second(0).unwrap().with_nanosecond(0).unwrap();
220
221                        if dt > n && dt_window.date_naive() == n.date_naive() && dt_time == n_time {
222                            ret.push(item.clone());
223                            break;
224                        }
225                    }
226                }
227            }
228        }
229
230        Ok(ret)
231    }
232
233    async fn complete_task(&mut self, primary_key: u64) -> Result<()> {
234        for record in self.records.values_mut() {
235            if record.primary_key() == primary_key {
236                record.set_completed(true);
237            }
238        }
239
240        Ok(())
241    }
242
243    async fn get(&mut self, primary_key: u64) -> Result<Record> {
244        let mut record: Option<Record> = None;
245        for r in self.records.values() {
246            if primary_key == r.primary_key() {
247                record = Some(r.clone());
248                break;
249            }
250        }
251
252        record.ok_or(anyhow!("No Record Found"))
253    }
254
255    async fn get_recurring(&mut self, recurrence_key: u64) -> Result<RecurringRecord> {
256        self.recurring
257            .get(&recurrence_key)
258            .ok_or(anyhow!("No Record Found"))
259            .cloned()
260    }
261
262    async fn update(&mut self, record: Record) -> Result<()> {
263        self.records.insert(record.primary_key(), record);
264        Ok(())
265    }
266
267    async fn update_recurring(&mut self, record: RecurringRecord) -> Result<()> {
268        self.recurring.insert(record.recurrence_key(), record);
269        Ok(())
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    #[tokio::test]
276    async fn test_recording() {
277        use crate::db::{memory::MemoryDB, unixfile::UnixFileLoader, DB};
278        use crate::record::Record;
279
280        let mut db = MemoryDB::new();
281
282        for x in 0..(rand::random::<u64>() % 50) + 1 {
283            assert!(db
284                .record(
285                    Record::build()
286                        .set_primary_key(x)
287                        .set_date(
288                            chrono::NaiveDate::from_ymd_opt(
289                                rand::random::<i32>() % 5 + 2023,
290                                rand::random::<u32>() % 12 + 1,
291                                rand::random::<u32>() % 28 + 1,
292                            )
293                            .unwrap(),
294                        )
295                        .set_at(Some(
296                            chrono::NaiveTime::from_hms_opt(
297                                rand::random::<u32>() % 24,
298                                rand::random::<u32>() % 60,
299                                0,
300                            )
301                            .unwrap(),
302                        ))
303                        .clone(),
304                )
305                .await
306                .is_ok());
307        }
308
309        let f = tempfile::NamedTempFile::new().unwrap();
310        assert!(UnixFileLoader::new(&f.path().to_path_buf())
311            .dump(db.clone())
312            .await
313            .is_ok());
314
315        let db2: MemoryDB = UnixFileLoader::new(&f.path().to_path_buf())
316            .load()
317            .await
318            .unwrap();
319        assert_eq!(db.primary_key, db2.primary_key);
320        assert_eq!(db.records, db2.records);
321    }
322}