use std::io;
use std::path::{Path, PathBuf};
use anyhow::Context;
use distant_core::net::common::ConnectionId;
use serde::{Deserialize, Serialize};
use crate::constants::user::CACHE_FILE_PATH;
mod id;
pub use id::CacheId;
#[derive(Clone, Debug)]
pub struct Cache {
file: CacheFile,
pub data: CacheData,
}
impl Cache {
pub fn new(custom_path: impl Into<Option<PathBuf>>) -> Self {
Self {
file: CacheFile::new(custom_path),
data: CacheData::default(),
}
}
pub async fn read_from_disk_or_default(
custom_path: impl Into<Option<PathBuf>>,
) -> anyhow::Result<Self> {
let file = CacheFile::new(custom_path);
let data = file.read_or_default().await?;
Ok(Self { file, data })
}
pub async fn write_to_disk(&self) -> anyhow::Result<()> {
self.file.write(&self.data).await
}
}
#[derive(Clone, Debug)]
pub struct CacheFile {
path: PathBuf,
}
impl CacheFile {
pub fn new(custom_path: impl Into<Option<PathBuf>>) -> Self {
Self {
path: custom_path
.into()
.unwrap_or_else(|| CACHE_FILE_PATH.to_path_buf()),
}
}
async fn read_or_default(&self) -> anyhow::Result<CacheData> {
CacheData::read_or_default(self.path.as_path())
.await
.with_context(|| format!("Failed to read cache from {:?}", self.path.as_path()))
}
async fn write(&self, data: &CacheData) -> anyhow::Result<()> {
data.write(self.path.as_path())
.await
.with_context(|| format!("Failed to write cache to {:?}", self.path.as_path()))
}
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct CacheData {
pub selected: CacheId<ConnectionId>,
}
impl CacheData {
async fn read(path: impl AsRef<Path>) -> io::Result<Self> {
let bytes = tokio::fs::read(path).await?;
toml_edit::de::from_slice(&bytes).map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))
}
async fn read_or_default(path: impl AsRef<Path>) -> io::Result<Self> {
match Self::read(path).await {
Ok(cache) => Ok(cache),
Err(x) if x.kind() == io::ErrorKind::NotFound => Ok(Self::default()),
Err(x) => Err(x),
}
}
async fn write(&self, path: impl AsRef<Path>) -> io::Result<()> {
let bytes = toml_edit::ser::to_vec(self)
.map_err(|x| io::Error::new(io::ErrorKind::InvalidData, x))?;
if let Some(parent) = path.as_ref().parent() {
tokio::fs::create_dir_all(parent).await?;
}
tokio::fs::write(path, bytes).await
}
}