use crate::component::Component;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{fs, io};
use tracing::instrument;
use walkdir::WalkDir;
pub type Result<T> = std::result::Result<T, self::Error>;
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("An I/O error occurred, faulty path: {faulty_path:?}")]
Io {
source: io::Error,
faulty_path: Option<PathBuf>,
},
#[error(transparent)]
SerdeYml(#[from] serde_yml::Error),
#[error(transparent)]
SerdeJson(#[from] serde_json::Error),
#[error(transparent)]
Zip(#[from] zip::result::ZipError),
#[error(transparent)]
Walkdir(#[from] walkdir::Error),
}
pub trait PersistedEntity: Serialize + for<'de> Deserialize<'de> {
const FILE_PATH: &'static str;
#[instrument]
fn read() -> Result<Self> {
let path = find_and_expand(Path::new(Self::FILE_PATH))?;
let yaml = fs::read_to_string(&path).map_err(|source| Error::Io {
source,
faulty_path: Some(path.clone()),
})?;
let entity = serde_yml::from_str(&yaml)?;
Ok(entity)
}
#[must_use = "You haven't checked if the entity was successfully persisted"]
#[instrument(skip(self))]
fn write(&self) -> Result<()> {
let path = PathBuf::from(Self::FILE_PATH);
let yaml = serde_yml::to_string(self)?;
fs::write(&path, yaml).map_err(|source| Error::Io {
source,
faulty_path: Some(path.clone()),
})?;
Ok(())
}
}
pub fn metadata_files<P>(path: P) -> Result<impl Iterator<Item = walkdir::DirEntry>>
where
P: AsRef<Path>,
{
let iterator = WalkDir::new(path.as_ref())
.into_iter()
.collect::<std::result::Result<Vec<_>, _>>()?
.into_iter()
.filter(|file| file.file_type().is_file())
.filter(|file| {
file.path()
.to_str()
.is_some_and(|path| path.ends_with(Component::LOCAL_STORAGE_SUFFIX))
});
Ok(iterator)
}
pub fn try_sync() -> Result<()> {
match std::process::Command::new("sync").status() {
Ok(_) => Ok(()),
Err(source) => Err(Error::Io {
source,
faulty_path: None,
}),
}
}
fn find_and_expand(path: &Path) -> Result<PathBuf> {
path.canonicalize().map_err(|source| Error::Io {
source,
faulty_path: Some(path.to_path_buf()),
})
}