use std::path::PathBuf;
use anyhow::{anyhow, Context};
use super::*;
pub struct JsonDataProvide {
file_path: PathBuf,
}
impl JsonDataProvide {
pub fn new(file_path: PathBuf) -> Self {
Self { file_path }
}
}
#[async_trait]
impl DataProvider for JsonDataProvide {
async fn load_all_entries(&self) -> anyhow::Result<Vec<Entry>> {
if !self.file_path.try_exists()? {
return Ok(Vec::new());
}
let json_content = tokio::fs::read_to_string(&self.file_path).await?;
if json_content.is_empty() {
return Ok(Vec::new());
}
let entries =
serde_json::from_str(&json_content).context("Error while parsing entries json data")?;
Ok(entries)
}
async fn add_entry(&self, entry: EntryDraft) -> Result<Entry, ModifyEntryError> {
if entry.title.is_empty() {
return Err(ModifyEntryError::ValidationError(
"Entry title can't be empty".into(),
));
}
let mut entries = self.load_all_entries().await?;
entries.sort_by_key(|e| e.id);
let id: u32 = entries.last().map(|entry| entry.id + 1).unwrap_or(0);
let new_entry = Entry::from_draft(id, entry);
entries.push(new_entry);
self.write_entries_to_file(&entries)
.await
.map_err(|err| anyhow!(err))?;
Ok(entries.into_iter().last().unwrap())
}
async fn remove_entry(&self, entry_id: u32) -> anyhow::Result<()> {
let mut entries = self.load_all_entries().await?;
if let Some(pos) = entries.iter().position(|e| e.id == entry_id) {
entries.remove(pos);
self.write_entries_to_file(&entries)
.await
.map_err(|err| anyhow!(err))?;
}
Ok(())
}
async fn update_entry(&self, entry: Entry) -> Result<Entry, ModifyEntryError> {
if entry.title.is_empty() {
return Err(ModifyEntryError::ValidationError(
"Entry title can't be empty".into(),
));
}
let mut entries = self.load_all_entries().await?;
if let Some(entry_to_modify) = entries.iter_mut().find(|e| e.id == entry.id) {
*entry_to_modify = entry.clone();
self.write_entries_to_file(&entries)
.await
.map_err(|err| anyhow!(err))?;
Ok(entry)
} else {
Err(ModifyEntryError::ValidationError(
"Entry title can't be empty".into(),
))
}
}
async fn get_export_object(&self, entries_ids: &[u32]) -> anyhow::Result<EntriesDTO> {
let entries: Vec<EntryDraft> = self
.load_all_entries()
.await?
.into_iter()
.filter(|entry| entries_ids.contains(&entry.id))
.map(EntryDraft::from_entry)
.collect();
Ok(EntriesDTO::new(entries))
}
async fn import_entries(&self, entries_dto: EntriesDTO) -> anyhow::Result<()> {
debug_assert_eq!(
TRANSFER_DATA_VERSION, entries_dto.version,
"Version mismatches check if there is a need to do a converting to the data"
);
for entry_darft in entries_dto.entries {
self.add_entry(entry_darft).await?;
}
Ok(())
}
}
impl JsonDataProvide {
async fn write_entries_to_file(&self, entries: &Vec<Entry>) -> anyhow::Result<()> {
let entries_text = serde_json::to_vec(&entries)?;
if !self.file_path.exists() {
if let Some(parent) = self.file_path.parent() {
tokio::fs::create_dir_all(parent).await?;
}
}
tokio::fs::write(&self.file_path, entries_text).await?;
Ok(())
}
}