use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
use std::time::Duration;
use super::FsAccess;
use crate::orchestration::CapabilityPolicy;
pub(super) const MEMO_TTL: Duration = Duration::from_millis(500);
pub(super) struct ScopeRoots {
pub(super) workspace: Vec<PathBuf>,
pub(super) read_only: Vec<PathBuf>,
pub(super) read_deny: Vec<PathBuf>,
}
#[derive(Clone, PartialEq, Eq)]
struct MemoKey {
workspace_roots: Vec<String>,
read_only_roots: Vec<String>,
read_deny_roots: Vec<String>,
anchor_roots: Option<Vec<PathBuf>>,
project_root: Option<PathBuf>,
execution_root: PathBuf,
home: Option<PathBuf>,
}
impl MemoKey {
fn current(policy: &CapabilityPolicy) -> Self {
Self {
workspace_roots: policy.workspace_roots.clone(),
read_only_roots: policy.read_only_roots.clone(),
read_deny_roots: policy.process_sandbox.read_deny_roots.clone(),
anchor_roots: super::current_session_anchor_workspace_roots(),
project_root: super::project_root_workspace_root(),
execution_root: crate::stdlib::process::execution_root_path(),
home: crate::user_dirs::home_dir(),
}
}
}
thread_local! {
static MEMO: RefCell<Option<(MemoKey, i128, Rc<ScopeRoots>)>> = const { RefCell::new(None) };
#[cfg(test)]
static DERIVATIONS: std::cell::Cell<usize> = const { std::cell::Cell::new(0) };
}
fn now_ms() -> i128 {
crate::clock_mock::instant_now().as_millis() as i128
}
fn derive(policy: &CapabilityPolicy) -> Rc<ScopeRoots> {
#[cfg(test)]
DERIVATIONS.with(|count| count.set(count.get() + 1));
Rc::new(ScopeRoots {
workspace: super::normalized_workspace_roots(policy),
read_only: super::normalized_read_only_roots(policy),
read_deny: super::process_sandbox_read_deny_roots(policy),
})
}
pub(super) fn scope_roots(policy: &CapabilityPolicy, access: FsAccess) -> Rc<ScopeRoots> {
if access != FsAccess::Read {
MEMO.with(|memo| memo.borrow_mut().take());
return derive(policy);
}
let key = MemoKey::current(policy);
let reused = MEMO.with(|memo| {
memo.borrow()
.as_ref()
.filter(|(memo_key, at, _)| {
*memo_key == key && now_ms() - *at < MEMO_TTL.as_millis() as i128
})
.map(|(_, _, roots)| Rc::clone(roots))
});
if let Some(roots) = reused {
return roots;
}
let roots = derive(policy);
MEMO.with(|memo| *memo.borrow_mut() = Some((key, now_ms(), Rc::clone(&roots))));
roots
}
#[cfg(test)]
pub(super) fn derivations() -> usize {
DERIVATIONS.with(std::cell::Cell::get)
}
#[cfg(test)]
pub(super) fn age(by: Duration) {
MEMO.with(|memo| {
if let Some((_, at, _)) = memo.borrow_mut().as_mut() {
*at -= by.as_millis() as i128;
}
});
}
#[cfg(test)]
pub(super) fn clear() {
MEMO.with(|memo| memo.borrow_mut().take());
}