use std::{
fs::{self, File, OpenOptions},
path::PathBuf,
sync::LazyLock,
};
use kodik_parser::KODIK_STATE;
use serde::{Deserialize, Serialize};
pub static CACHE_PATH: LazyLock<Option<PathBuf>> =
LazyLock::new(|| dirs::cache_dir().map(|cache_dir| cache_dir.join("kodik").join("cache.json")));
#[derive(Debug, Serialize, Deserialize)]
pub struct Cache {
pub shift: Option<u8>,
pub endpoint: Option<String>,
#[serde(skip)]
pub path: PathBuf,
}
impl Cache {
pub fn load() -> Option<Self> {
let cache_path = CACHE_PATH.as_ref()?;
if !cache_path.exists() {
if let Some(parent) = cache_path.parent() {
let _ = fs::create_dir_all(parent);
}
let _ = File::create(cache_path);
}
match fs::read_to_string(cache_path) {
Ok(content) => {
let mut cache = serde_json::from_str::<Self>(&content).ok()?;
cache.path.clone_from(cache_path);
Some(cache)
}
Err(_) => None,
}
}
pub fn save(&self) -> Option<()> {
let cache_path = CACHE_PATH.as_ref()?;
let file = OpenOptions::new().write(true).truncate(true).open(cache_path).ok()?;
serde_json::to_writer_pretty(file, self).ok()
}
pub fn update(&mut self) {
self.shift = Some(KODIK_STATE.shift());
self.endpoint = Some(KODIK_STATE.endpoint().to_string());
}
pub fn is_changed(&self) -> bool {
self.shift != Some(KODIK_STATE.shift()) || self.endpoint.as_deref() != Some(&*KODIK_STATE.endpoint())
}
pub fn apply(&self) {
if let Some(shift) = self.shift {
KODIK_STATE.set_shift(shift);
}
if let Some(endpoint) = self.endpoint.clone() {
KODIK_STATE.set_endpoint(endpoint);
}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::indexing_slicing)]
mod tests {
use super::*;
fn load_test() -> Cache {
let mut cache = Cache::load().unwrap();
if cache.endpoint.as_ref().unwrap().is_empty() || cache.shift.unwrap() == 0 {
let cache_path = CACHE_PATH.as_ref().unwrap();
cache = Cache {
shift: Some(13),
endpoint: Some(String::from("/abcd")),
path: CACHE_PATH.as_ref().unwrap().to_owned(),
};
let file = OpenOptions::new().write(true).open(cache_path).unwrap();
serde_json::to_writer_pretty(file, &cache).unwrap();
}
cache
}
#[test]
fn apply_test() {
let cache = load_test();
assert!(KODIK_STATE.endpoint().is_empty());
assert_eq!(KODIK_STATE.shift(), 0);
cache.apply();
assert!(!KODIK_STATE.endpoint().is_empty());
assert_ne!(KODIK_STATE.shift(), 0);
}
}