dynasty 1.4.1

Dynasty Reader's CLI downloader
Documentation
use std::{
    fs::{self, File},
    future::Future,
    io::BufWriter,
    path::PathBuf,
};

use anyhow::Result;
use directories::ProjectDirs;
use serde::{de::DeserializeOwned, Serialize};

#[derive(Debug, Clone)]
pub struct CacheManager {
    path: PathBuf,
}

impl CacheManager {
    pub fn new() -> Result<CacheManager> {
        let path = ProjectDirs::from("", "", "dynasty-rs")
            .map(|project| project.cache_dir().to_path_buf())
            .unwrap_or_else(|| PathBuf::from("./dynasty-cache"));

        if !path.exists() {
            fs::create_dir_all(&path)?;
        }

        Ok(CacheManager { path })
    }

    fn get_cache_path(&self, identifier: &str) -> PathBuf {
        self.path.join(format!(
            "{}.json",
            identifier
                .to_lowercase()
                .replace('/', "_")
                .replace(' ', "_")
        ))
    }

    fn get_cache<T: DeserializeOwned + Serialize>(&self, identifier: &str) -> Option<T> {
        let cache_path = self.get_cache_path(identifier);

        fs::read_to_string(cache_path)
            .ok()
            .and_then(|content| serde_json::from_str(&content).ok())
    }

    fn put_cache<T: DeserializeOwned + Serialize>(
        &self,
        identifier: &str,
        value: &T,
    ) -> Result<()> {
        let cache_path = self.get_cache_path(identifier);

        let file = File::create(cache_path)?;
        let writer = BufWriter::new(file);
        serde_json::to_writer(writer, value)?;

        Ok(())
    }

    pub async fn get_or_init<T: DeserializeOwned + Serialize>(
        &self,
        cache_identifier: &str,
        future_init: impl Future<Output = Result<T>>,
    ) -> Result<T> {
        if let Some(cached_value) = self.get_cache::<T>(cache_identifier) {
            Ok(cached_value)
        } else {
            let value = future_init.await?;
            self.put_cache(cache_identifier, &value)?;

            Ok(value)
        }
    }
}