use crate::models::error::WayclipError;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
use std::{collections::HashMap, path::PathBuf};
pub const USERS_ME_DATA_KEY: &'static str = "users_me_data";
pub const USERS_ME_DATA_TTL: i64 = 3600;
pub struct Cache<T> {
cache_path: PathBuf,
entries: HashMap<String, CachedData<T>>,
}
impl<T: Serialize + DeserializeOwned + Clone> Cache<T> {
pub async fn init() -> Result<Self, WayclipError> {
let cache_path = dirs::cache_dir()
.ok_or_else(|| WayclipError::NotFound("No cache dir found".into()))?
.join("wayclip")
.join("data.json");
let bytes = tokio::fs::read(&cache_path).await?;
let entries = serde_json::from_slice::<HashMap<String, CachedData<T>>>(&bytes)?;
Ok(Self {
cache_path,
entries,
})
}
pub async fn insert_item(
&mut self,
key: String,
data: T,
ttl: i64,
) -> Result<CachedData<T>, WayclipError> {
let cached_data = CachedData::new(data, ttl);
self.entries.insert(key, cached_data.clone());
let bytes = serde_json::to_vec(&self.entries)?;
tokio::fs::write(&self.cache_path, &bytes).await?;
Ok(cached_data)
}
pub async fn get_item(&mut self, key: String) -> Result<Option<T>, WayclipError> {
match self.entries.get(&key) {
Some(cached_data) => match cached_data.get() {
Some(data) => Ok(Some(data)),
None => {
self.entries.remove(&key);
let bytes = serde_json::to_vec(&self.entries)?;
tokio::fs::write(&self.cache_path, &bytes).await?;
Ok(None)
}
},
None => Ok(None),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound(serialize = "T: Serialize", deserialize = "T: DeserializeOwned"))]
pub struct CachedData<T> {
pub data: T,
pub ttl: i64,
pub created_at: DateTime<Utc>,
}
impl<T: Serialize + DeserializeOwned + Clone> CachedData<T> {
pub fn new(data: T, ttl: i64) -> Self {
Self {
data,
ttl,
created_at: chrono::Utc::now(),
}
}
pub fn get(&self) -> Option<T> {
if chrono::Utc::now()
.signed_duration_since(self.created_at)
.num_seconds()
< self.ttl
{
Some(self.data.clone())
} else {
None
}
}
}