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}