use super::wire::{PiiFinding, PiiHold};
use crate::app::WriteRequest;
use cyberbrain_policy::Finding;
use std::collections::HashMap;
use std::sync::Mutex;
pub const HOLD_TTL: jiff::SignedDuration = jiff::SignedDuration::from_secs(15 * 60);
#[derive(Debug, Clone)]
pub struct HeldWrite {
pub hold: PiiHold,
pub request: WriteRequest,
pub findings: Vec<Finding>,
pub created: bool,
}
#[derive(Default)]
pub struct Holds {
inner: Mutex<HashMap<String, HeldWrite>>,
}
impl Holds {
pub fn new() -> Self {
Self::default()
}
fn prune(map: &mut HashMap<String, HeldWrite>) {
let now = jiff::Timestamp::now();
map.retain(|_, h| h.hold.expires_at > now);
}
pub fn insert(
&self,
request: WriteRequest,
findings: Vec<Finding>,
created: bool,
rendered: Vec<PiiFinding>,
) -> PiiHold {
let hold = PiiHold {
hold_id: ulid::Ulid::generate().to_string(),
note: request.name.clone(),
findings: rendered,
expires_at: jiff::Timestamp::now() + HOLD_TTL,
dry_run: request.dry_run,
};
let mut map = self.inner.lock().unwrap_or_else(|p| p.into_inner());
Self::prune(&mut map);
map.insert(
hold.hold_id.clone(),
HeldWrite {
hold: hold.clone(),
request,
findings,
created,
},
);
hold
}
pub fn take(&self, id: &str) -> Option<HeldWrite> {
let mut map = self.inner.lock().unwrap_or_else(|p| p.into_inner());
Self::prune(&mut map);
map.remove(id)
}
pub fn list(&self) -> Vec<PiiHold> {
let mut map = self.inner.lock().unwrap_or_else(|p| p.into_inner());
Self::prune(&mut map);
let mut out: Vec<PiiHold> = map.values().map(|h| h.hold.clone()).collect();
out.sort_by_key(|h| h.expires_at);
out
}
}