//! The pending-approval table: whoever takes an entry out answers it, and everyone else finds it //! gone. In memory only. use crate::policy::{Ask, Decision}; use proto::{DenyReason, PendingApproval, Timestamp}; use std::collections::BTreeMap; use std::sync::mpsc::{Receiver, Sender, channel}; use std::sync::{Mutex, MutexGuard}; /// What the waiting thread is told. Boxed: clippy's `large_enum_variant` rejects it unboxed. #[derive(Debug)] pub enum Verdict { Run(Box), Denied(DenyReason), } #[derive(Debug)] pub struct Entry { pub info: PendingApproval, pub ask: Ask, pub reply: Sender, } #[derive(Debug, Default)] pub struct Table { entries: Mutex>, } impl Table { pub fn new() -> Table { Table { entries: Mutex::new(BTreeMap::new()), } } fn lock(&self) -> MutexGuard<'_, BTreeMap> { self.entries .lock() .unwrap_or_else(|poisoned| poisoned.into_inner()) } pub fn insert(&self, info: PendingApproval, ask: Ask) -> Receiver { let (sender, receiver) = channel(); self.lock().insert( info.approval, Entry { info, ask, reply: sender, }, ); receiver } pub fn take(&self, id: u64) -> Option { self.lock().remove(&id) } pub fn take_expired(&self, now: Timestamp) -> Vec { // One lock: taking it once per id would let an approve slip in between. let mut entries = self.lock(); let ids: Vec = entries .values() .filter(|entry| now >= entry.info.expires) .map(|entry| entry.info.approval) .collect(); ids.iter().filter_map(|id| entries.remove(id)).collect() } pub fn list(&self) -> Vec { self.lock() .values() .map(|entry| entry.info.clone()) .collect() } }