Skip to main content

backend/
json.rs

1use std::path::PathBuf;
2
3use anyhow::Context;
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.exists() {
20            return Ok(Vec::new());
21        }
22
23        let json_content = tokio::fs::read_to_string(&self.file_path)
24            .await
25            .with_context(|| {
26                format!("Failed to read entries file: {}", self.file_path.display())
27            })?;
28
29        if json_content.is_empty() {
30            return Ok(Vec::new());
31        }
32
33        let entries = serde_json::from_str(&json_content).with_context(|| {
34            format!("Failed to parse entries file: {}", self.file_path.display())
35        })?;
36
37        Ok(entries)
38    }
39
40    async fn add_entry(&self, entry: EntryDraft) -> Result<Entry, ModifyEntryError> {
41        if entry.title.is_empty() {
42            return Err(ModifyEntryError::ValidationError(
43                "Entry title can't be empty".into(),
44            ));
45        }
46
47        let mut entries = self.load_all_entries().await?;
48
49        let id: u32 = entries.iter().map(|e| e.id + 1).max().unwrap_or(0);
50
51        let new_entry = Entry::from_draft(id, entry);
52
53        entries.push(new_entry);
54
55        self.write_entries_to_file(&entries).await?;
56
57        Ok(entries.into_iter().next_back().unwrap())
58    }
59
60    async fn restore_entry(&self, entry: Entry) -> Result<Entry, ModifyEntryError> {
61        if entry.title.is_empty() {
62            return Err(ModifyEntryError::ValidationError(
63                "Entry title can't be empty".into(),
64            ));
65        }
66
67        let mut entries = self.load_all_entries().await?;
68        if entries.iter().any(|existing| existing.id == entry.id) {
69            return Err(ModifyEntryError::ValidationError(format!(
70                "Entry id {} already exists",
71                entry.id
72            )));
73        }
74
75        entries.push(entry.clone());
76        self.write_entries_to_file(&entries).await?;
77
78        Ok(entry)
79    }
80
81    async fn remove_entry(&self, entry_id: u32) -> anyhow::Result<()> {
82        let mut entries = self.load_all_entries().await?;
83
84        if let Some(pos) = entries.iter().position(|e| e.id == entry_id) {
85            entries.remove(pos);
86
87            self.write_entries_to_file(&entries).await?;
88        }
89
90        Ok(())
91    }
92
93    async fn update_entry(&self, entry: Entry) -> Result<Entry, ModifyEntryError> {
94        if entry.title.is_empty() {
95            return Err(ModifyEntryError::ValidationError(
96                "Entry title can't be empty".into(),
97            ));
98        }
99
100        let mut entries = self.load_all_entries().await?;
101
102        if let Some(entry_to_modify) = entries.iter_mut().find(|e| e.id == entry.id) {
103            *entry_to_modify = entry.clone();
104
105            self.write_entries_to_file(&entries).await?;
106
107            Ok(entry)
108        } else {
109            Err(ModifyEntryError::ValidationError(
110                "Entry title can't be empty".into(),
111            ))
112        }
113    }
114
115    async fn get_export_object(&self, entries_ids: &[u32]) -> anyhow::Result<EntriesDTO> {
116        let entries: Vec<EntryDraft> = self
117            .load_all_entries()
118            .await?
119            .into_iter()
120            .filter(|entry| entries_ids.contains(&entry.id))
121            .map(EntryDraft::from_entry)
122            .collect();
123
124        Ok(EntriesDTO::new(entries))
125    }
126
127    async fn assign_priority_to_entries(&self, priority: u32) -> anyhow::Result<()> {
128        let mut entries = self.load_all_entries().await?;
129
130        let mut modified = false;
131
132        entries
133            .iter_mut()
134            .filter(|entry| entry.priority.is_none())
135            .for_each(|entry| {
136                entry.priority = Some(priority);
137                modified = true;
138            });
139
140        if modified {
141            self.write_entries_to_file(&entries).await?;
142        }
143
144        Ok(())
145    }
146}
147
148impl JsonDataProvide {
149    async fn write_entries_to_file(&self, entries: &Vec<Entry>) -> anyhow::Result<()> {
150        let entries_text = serde_json::to_vec(&entries).with_context(|| {
151            format!(
152                "Failed to serialize entries for {}",
153                self.file_path.display()
154            )
155        })?;
156        if !self.file_path.exists()
157            && let Some(parent) = self.file_path.parent()
158        {
159            tokio::fs::create_dir_all(parent).await.with_context(|| {
160                format!("Failed to create entries directory: {}", parent.display())
161            })?;
162        }
163        tokio::fs::write(&self.file_path, entries_text)
164            .await
165            .with_context(|| {
166                format!("Failed to write entries file: {}", self.file_path.display())
167            })?;
168
169        Ok(())
170    }
171}