terminal-info 1.4.2

An extensible terminal information CLI and developer toolbox
Documentation
use std::env;
use std::fs;
use std::path::PathBuf;
use std::time::{SystemTime, UNIX_EPOCH};

use serde::{Deserialize, Serialize, de::DeserializeOwned};

use crate::config::home_dir_path;

#[derive(Serialize, Deserialize)]
struct CacheEntry<T> {
    timestamp: u64,
    data: T,
}

pub fn read_cache<T: DeserializeOwned>(key: &str, ttl_secs: u64) -> Option<T> {
    let path = cache_file_path(key).ok()?;
    let contents = fs::read_to_string(path).ok()?;
    let entry: CacheEntry<T> = serde_json::from_str(&contents).ok()?;
    if now_unix().saturating_sub(entry.timestamp) <= ttl_secs {
        Some(entry.data)
    } else {
        None
    }
}

pub fn write_cache<T: Serialize>(key: &str, data: &T) -> Result<(), String> {
    let path = cache_file_path(key)?;
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)
            .map_err(|err| format!("Failed to create cache directory: {err}"))?;
    }

    let entry = CacheEntry {
        timestamp: now_unix(),
        data,
    };
    let json = serde_json::to_string_pretty(&entry)
        .map_err(|err| format!("Failed to serialize cache entry: {err}"))?;
    fs::write(path, format!("{json}\n"))
        .map_err(|err| format!("Failed to write cache entry: {err}"))
}

pub fn cache_file_path(key: &str) -> Result<PathBuf, String> {
    let base = cache_dir_path()?;
    Ok(base.join(format!("{key}.json")))
}

pub fn cache_dir_path() -> Result<PathBuf, String> {
    if let Ok(dir) = env::var("TINFO_CACHE_DIR") {
        return Ok(PathBuf::from(dir));
    }

    Ok(home_dir_path().join(".tinfo").join("cache"))
}

pub fn now_unix() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default()
        .as_secs()
}