use std::collections::HashMap;
use std::sync::Mutex;
use super::Decision;
#[derive(Debug, Default)]
pub struct SessionCache {
inner: Mutex<HashMap<String, Decision>>,
}
impl SessionCache {
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, key: &str) -> Option<Decision> {
self.inner.lock().ok()?.get(key).cloned()
}
pub fn insert(&self, key: String, decision: Decision) {
if let Ok(mut guard) = self.inner.lock() {
guard.insert(key, decision);
}
}
pub fn key(tool: &str, args: &serde_json::Value) -> String {
let fingerprint = fingerprint(args);
format!("{tool}::{fingerprint}")
}
}
fn fingerprint(args: &serde_json::Value) -> String {
let s = serde_json::to_string(args).unwrap_or_default();
let mut hasher = siphasher::sip::SipHasher13::new();
std::hash::Hash::hash(&s, &mut hasher);
format!("{:016x}", std::hash::Hasher::finish(&hasher))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::permissions::Decision;
#[test]
fn cache_round_trip() {
let c = SessionCache::new();
let key = SessionCache::key("bash", &serde_json::json!({"command":"git status"}));
c.insert(key.clone(), Decision::Allowed);
assert!(matches!(c.get(&key), Some(Decision::Allowed)));
}
#[test]
fn key_differs_by_args() {
let k1 = SessionCache::key("bash", &serde_json::json!({"command":"git status"}));
let k2 = SessionCache::key("bash", &serde_json::json!({"command":"git diff"}));
assert_ne!(k1, k2);
}
}