use crate::crdt::{TaskList, TaskListId};
use std::path::PathBuf;
use tokio::fs;
#[derive(Debug, Clone)]
pub struct TaskListStorage {
storage_path: PathBuf,
}
impl TaskListStorage {
#[must_use]
pub fn new(storage_path: PathBuf) -> Self {
Self { storage_path }
}
pub async fn save_task_list(
&self,
list_id: &TaskListId,
task_list: &TaskList,
) -> crate::crdt::error::Result<()> {
fs::create_dir_all(&self.storage_path).await?;
let serialized =
bincode::serialize(task_list).map_err(crate::crdt::error::CrdtError::Serialization)?;
let file_path = self.list_file_path(list_id);
let temp_path = file_path.with_extension("tmp");
fs::write(&temp_path, &serialized).await?;
fs::rename(&temp_path, &file_path).await?;
Ok(())
}
pub async fn load_task_list(
&self,
list_id: &TaskListId,
) -> crate::crdt::error::Result<TaskList> {
let file_path = self.list_file_path(list_id);
let serialized = fs::read(&file_path).await?;
bincode::deserialize(&serialized).map_err(crate::crdt::error::CrdtError::Serialization)
}
pub async fn list_task_lists(&self) -> crate::crdt::error::Result<Vec<String>> {
if !self.storage_path.exists() {
return Ok(Vec::new());
}
let mut dir_entries = fs::read_dir(&self.storage_path).await?;
let mut list_ids = Vec::new();
while let Some(entry) = dir_entries.next_entry().await? {
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "tmp") {
continue;
}
if path.extension().is_some_and(|ext| ext == "bin") {
if let Some(file_name) = path.file_stem() {
if let Some(id_str) = file_name.to_str() {
list_ids.push(id_str.to_string());
}
}
}
}
Ok(list_ids)
}
pub async fn delete_task_list(&self, list_id: &TaskListId) -> crate::crdt::error::Result<()> {
let file_path = self.list_file_path(list_id);
fs::remove_file(file_path).await?;
Ok(())
}
fn list_file_path(&self, list_id: &TaskListId) -> PathBuf {
self.storage_path.join(format!("{}.bin", list_id))
}
}