backend/
json.rs

1use std::path::PathBuf;
2
3use anyhow::{Context, anyhow};
4
5use super::*;
6
7pub struct JsonDataProvide {
8    file_path: PathBuf,
9}
10
11impl JsonDataProvide {
12    pub fn new(file_path: PathBuf) -> Self {
13        Self { file_path }
14    }
15}
16
17impl DataProvider for JsonDataProvide {
18    async fn load_all_entries(&self) -> anyhow::Result<Vec<Entry>> {
19        if !self.file_path.try_exists()? {
20            return Ok(Vec::new());
21        }
22
23        let json_content = tokio::fs::read_to_string(&self.file_path).await?;
24        if json_content.is_empty() {
25            return Ok(Vec::new());
26        }
27        let entries =
28            serde_json::from_str(&json_content).context("Error while parsing entries json data")?;
29
30        Ok(entries)
31    }
32
33    async fn add_entry(&self, entry: EntryDraft) -> Result<Entry, ModifyEntryError> {
34        if entry.title.is_empty() {
35            return Err(ModifyEntryError::ValidationError(
36                "Entry title can't be empty".into(),
37            ));
38        }
39
40        let mut entries = self.load_all_entries().await?;
41
42        entries.sort_by_key(|e| e.id);
43
44        let id: u32 = entries.last().map(|entry| entry.id + 1).unwrap_or(0);
45
46        let new_entry = Entry::from_draft(id, entry);
47
48        entries.push(new_entry);
49
50        self.write_entries_to_file(&entries)
51            .await
52            .map_err(|err| anyhow!(err))?;
53
54        Ok(entries.into_iter().next_back().unwrap())
55    }
56
57    async fn remove_entry(&self, entry_id: u32) -> anyhow::Result<()> {
58        let mut entries = self.load_all_entries().await?;
59
60        if let Some(pos) = entries.iter().position(|e| e.id == entry_id) {
61            entries.remove(pos);
62
63            self.write_entries_to_file(&entries)
64                .await
65                .map_err(|err| anyhow!(err))?;
66        }
67
68        Ok(())
69    }
70
71    async fn update_entry(&self, entry: Entry) -> Result<Entry, ModifyEntryError> {
72        if entry.title.is_empty() {
73            return Err(ModifyEntryError::ValidationError(
74                "Entry title can't be empty".into(),
75            ));
76        }
77
78        let mut entries = self.load_all_entries().await?;
79
80        if let Some(entry_to_modify) = entries.iter_mut().find(|e| e.id == entry.id) {
81            *entry_to_modify = entry.clone();
82
83            self.write_entries_to_file(&entries)
84                .await
85                .map_err(|err| anyhow!(err))?;
86
87            Ok(entry)
88        } else {
89            Err(ModifyEntryError::ValidationError(
90                "Entry title can't be empty".into(),
91            ))
92        }
93    }
94
95    async fn get_export_object(&self, entries_ids: &[u32]) -> anyhow::Result<EntriesDTO> {
96        let entries: Vec<EntryDraft> = self
97            .load_all_entries()
98            .await?
99            .into_iter()
100            .filter(|entry| entries_ids.contains(&entry.id))
101            .map(EntryDraft::from_entry)
102            .collect();
103
104        Ok(EntriesDTO::new(entries))
105    }
106
107    async fn assign_priority_to_entries(&self, priority: u32) -> anyhow::Result<()> {
108        let mut entries = self.load_all_entries().await?;
109
110        entries
111            .iter_mut()
112            .filter(|entry| entry.priority.is_none())
113            .for_each(|entry| entry.priority = Some(priority));
114
115        self.write_entries_to_file(&entries).await?;
116
117        Ok(())
118    }
119}
120
121impl JsonDataProvide {
122    async fn write_entries_to_file(&self, entries: &Vec<Entry>) -> anyhow::Result<()> {
123        let entries_text = serde_json::to_vec(&entries)?;
124        if !self.file_path.exists() {
125            if let Some(parent) = self.file_path.parent() {
126                tokio::fs::create_dir_all(parent).await?;
127            }
128        }
129        tokio::fs::write(&self.file_path, entries_text).await?;
130
131        Ok(())
132    }
133}