use std::{
collections::HashMap,
sync::{Arc, Mutex},
};
pub const DEFAULT_DELEGATION_DEPTH: usize = 2;
#[derive(Debug, Clone)]
pub(crate) struct Depth {
by_agent: Arc<Mutex<HashMap<String, usize>>>,
max: usize,
}
impl Default for Depth {
fn default() -> Self {
Self::new(DEFAULT_DELEGATION_DEPTH)
}
}
impl Depth {
pub(crate) fn new(max: usize) -> Self {
Self {
by_agent: Arc::new(Mutex::new(HashMap::new())),
max,
}
}
pub(crate) fn authorize_delegation(&self, agent_id: &str) -> Result<usize, String> {
let depth = self.of(agent_id);
if depth >= self.max {
return Err(refusal(depth, self.max));
}
Ok(depth)
}
pub(crate) fn entered(&self, agent_id: &str, depth: usize) -> Entered {
self.lock().insert(agent_id.to_string(), depth);
Entered {
by_agent: Arc::clone(&self.by_agent),
agent_id: agent_id.to_string(),
}
}
fn of(&self, agent_id: &str) -> usize {
self.lock().get(agent_id).copied().unwrap_or(0)
}
fn lock(&self) -> std::sync::MutexGuard<'_, HashMap<String, usize>> {
self.by_agent
.lock()
.unwrap_or_else(|error| error.into_inner())
}
}
pub(crate) struct Entered {
by_agent: Arc<Mutex<HashMap<String, usize>>>,
agent_id: String,
}
impl Drop for Entered {
fn drop(&mut self) {
self.by_agent
.lock()
.unwrap_or_else(|error| error.into_inner())
.remove(&self.agent_id);
}
}
fn refusal(depth: usize, max: usize) -> String {
format!(
"this work is already {depth} levels of delegation deep and spawn goes no deeper than \
{max}; do it here rather than handing it on"
)
}