mirror of https://github.com/chipsenkbeil/distant
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
3.4 KiB
Rust
110 lines
3.4 KiB
Rust
use crate::paths::user::CACHE_FILE_PATH;
|
|
use anyhow::Context;
|
|
use distant_core::ConnectionId;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::{
|
|
io,
|
|
path::{Path, PathBuf},
|
|
};
|
|
|
|
mod id;
|
|
pub use id::CacheId;
|
|
|
|
/// Represents a disk-backed cache of data
|
|
#[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(),
|
|
}
|
|
}
|
|
|
|
/// Loads the cache from the specified file path, or default user-local cache path,
|
|
/// constructing data from the default cache if not found
|
|
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 })
|
|
}
|
|
|
|
/// Writes the cache back to disk
|
|
pub async fn write_to_disk(&self) -> anyhow::Result<()> {
|
|
self.file.write(&self.data).await
|
|
}
|
|
}
|
|
|
|
/// Points to a cache file to support reading, writing, and editing the data
|
|
#[derive(Clone, Debug)]
|
|
pub struct CacheFile {
|
|
path: PathBuf,
|
|
}
|
|
|
|
impl CacheFile {
|
|
/// Creates a new [`CacheFile`] from the given path, defaulting to a user-local cache path
|
|
/// if none is provided
|
|
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()))
|
|
}
|
|
}
|
|
|
|
/// Provides quick access to cli-specific cache for a user
|
|
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
|
pub struct CacheData {
|
|
/// Connection id of selected connection (or 0 if nothing selected)
|
|
pub selected: CacheId<ConnectionId>,
|
|
}
|
|
|
|
impl CacheData {
|
|
/// Reads the cache data from disk
|
|
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))
|
|
}
|
|
|
|
/// Reads the cache data if the file exists, otherwise returning a default cache instance
|
|
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),
|
|
}
|
|
}
|
|
|
|
/// Writes the cache data to disk
|
|
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))?;
|
|
|
|
// Ensure the parent directory of the cache exists
|
|
if let Some(parent) = path.as_ref().parent() {
|
|
tokio::fs::create_dir_all(parent).await?;
|
|
}
|
|
|
|
tokio::fs::write(path, bytes).await
|
|
}
|
|
}
|