use std::{time::{SystemTime, Duration}, io::{self, BufReader, BufWriter, Write}, fmt, path::PathBuf, fs::{File, self}, ops::Add}; use serde_derive::{Serialize, Deserialize}; #[derive(Debug, Serialize, Deserialize)] pub struct SequenceFile { time: SystemTime, keys: Vec, } impl SequenceFile { pub fn empty() -> Self { Self { time: SystemTime::now(), keys: Vec::new() } } pub fn time(&self) -> SystemTime { self.time } pub fn keys(&self) -> &Vec { &self.keys } } impl From> for SequenceFile { fn from(value: Vec) -> Self { Self { time: SystemTime::now(), keys: value } } } pub struct SequenceCacher { cache_path: PathBuf, cache_metadata: SystemTime } pub enum CacheError { Expired, IO(io::Error), Serde(serde_json::Error) } impl fmt::Display for CacheError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { CacheError::Expired => write!(f, "cache has expired"), CacheError::IO(..) => write!(f, "an I/O error has occurred."), CacheError::Serde(..) => write!(f, "there was a problem with serialization or deserialization") } } } impl From for CacheError { fn from(err: io::Error) -> Self { CacheError::IO(err) } } impl From for CacheError { fn from(err: serde_json::Error) -> Self { CacheError::Serde(err) } } impl SequenceCacher { pub fn new(cache_path: &PathBuf, group_id: &str) -> Self { let file_path = cache_path .to_string_lossy() .replace("{group}", group_id); let file_path = PathBuf::from(file_path); Self { cache_path: file_path, cache_metadata: SystemTime::now() } } pub fn try_load(&self, debounce_time: Duration) -> Result { let file = File::open(&self.cache_path)?; let reader = BufReader::new(file); let read: SequenceFile = serde_json::from_reader(reader)?; let debounce_time = read.time.add(debounce_time); if SystemTime::now() > debounce_time { return Err(CacheError::Expired.into()); } Ok(read) } pub fn try_cache(&mut self, keys: Vec) -> Result<(), CacheError> { { let file = File::create(&self.cache_path)?; let mut writer = BufWriter::new(file); let data: SequenceFile = keys.into(); serde_json::to_writer(&mut writer, &data)?; writer.flush()?; } self.cache_metadata = fs::metadata(&self.cache_path)?.modified()?; Ok(()) } pub fn exists(&self) -> bool { self.cache_path.exists() } pub fn modified(&self) -> io::Result { if !self.exists() { Ok(true) } else { Ok(self.cache_metadata != fs::metadata(&self.cache_path)?.modified()?) } } pub fn remove(&self) -> io::Result<()> { if !self.exists() { Ok(()) } else { fs::remove_file(&self.cache_path) } } }