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}