Add the table of pending approvals

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
2026-09-19 16:48:09 -07:00
parent 57734ebb9a
commit caf8fd6eca
4 changed files with 238 additions and 1 deletions
+76
View File
@@ -0,0 +1,76 @@
//! 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<Decision>),
Denied(DenyReason),
}
#[derive(Debug)]
pub struct Entry {
pub info: PendingApproval,
pub ask: Ask,
pub reply: Sender<Verdict>,
}
#[derive(Debug, Default)]
pub struct Table {
entries: Mutex<BTreeMap<u64, Entry>>,
}
impl Table {
pub fn new() -> Table {
Table {
entries: Mutex::new(BTreeMap::new()),
}
}
fn lock(&self) -> MutexGuard<'_, BTreeMap<u64, Entry>> {
self.entries
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub fn insert(&self, info: PendingApproval, ask: Ask) -> Receiver<Verdict> {
let (sender, receiver) = channel();
self.lock().insert(
info.approval,
Entry {
info,
ask,
reply: sender,
},
);
receiver
}
pub fn take(&self, id: u64) -> Option<Entry> {
self.lock().remove(&id)
}
pub fn take_expired(&self, now: Timestamp) -> Vec<Entry> {
// One lock: taking it once per id would let an approve slip in between.
let mut entries = self.lock();
let ids: Vec<u64> = 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<PendingApproval> {
self.lock()
.values()
.map(|entry| entry.info.clone())
.collect()
}
}