pkgrisk 0.1.0

5-second package health and risk analysis for npm, PyPI and crates.io dependencies
use rusqlite::{params, Connection};
use std::path::PathBuf;

pub struct SqliteCache {
    conn: Connection,
}

impl SqliteCache {
    pub fn new(db_path: PathBuf) -> anyhow::Result<Self> {
        let conn = Connection::open(&db_path)?;
        
        conn.execute(
            "CREATE TABLE IF NOT EXISTS cache (
                key TEXT PRIMARY KEY,
                value TEXT NOT NULL,
                timestamp INTEGER NOT NULL
            )",
            [],
        )?;

        Ok(Self { conn })
    }

    pub fn get(&self, key: &str, ttl_secs: u64) -> anyhow::Result<Option<String>> {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)?
            .as_secs();

        let mut stmt = self.conn.prepare("SELECT value, timestamp FROM cache WHERE key = ?1")?;
        let mut rows = stmt.query(params![key])?;

        if let Some(row) = rows.next()? {
            let timestamp: i64 = row.get(1)?;
            if now as i64 - timestamp <= ttl_secs as i64 {
                let value: String = row.get(0)?;
                return Ok(Some(value));
            } else {
                self.conn.execute("DELETE FROM cache WHERE key = ?1", params![key])?;
            }
        }
        
        Ok(None)
    }

    pub fn set(&self, key: &str, value: &str) -> anyhow::Result<()> {
        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)?
            .as_secs();

        self.conn.execute(
            "INSERT OR REPLACE INTO cache (key, value, timestamp) VALUES (?1, ?2, ?3)",
            params![key, value, now as i64],
        )?;

        Ok(())
    }
}