Skip to main content

a3s_code_core/capability/
lease.rs

1use std::marker::PhantomData;
2
3use tokio_util::sync::CancellationToken;
4
5use super::scope::ScopeInner;
6use super::{
7    CapabilityCeiling, CapabilityDescriptor, CapabilityId, CapabilityScopeError, CapabilityScopeId,
8    CapabilityScopeKind, CodeCatalogGeneration, ScopeKind, Sha256Digest, UseCapabilityGeneration,
9};
10
11/// Host adapter for the exact non-clone lease returned by A3S Use.
12///
13/// Implementations retain the real
14/// `a3s_use::capability_registry::CapabilitySnapshotLease` (or an equivalent
15/// host wrapper) as part of `self`. Code consumes the value at Run admission
16/// and drops it only after every child, task, and effect has settled.
17pub trait RetainedUseGeneration: Send + Sync + 'static {
18    fn use_generation(&self) -> &UseCapabilityGeneration;
19}
20
21impl<T> RetainedUseGeneration for Box<T>
22where
23    T: RetainedUseGeneration + ?Sized,
24{
25    fn use_generation(&self) -> &UseCapabilityGeneration {
26        self.as_ref().use_generation()
27    }
28}
29
30/// Borrowed capability access tied to one typed scope owner.
31///
32/// A lease cannot be returned after its owner is dropped:
33///
34/// ```compile_fail
35/// use a3s_code_core::capability::{CapabilityLease, CapabilityScope, Run};
36///
37/// fn escape<'a>(scope: CapabilityScope<Run>) -> CapabilityLease<'a, Run> {
38///     scope.lease().unwrap()
39/// }
40/// ```
41///
42/// Marker types also prevent a narrower Turn lease from entering a Run-only
43/// API:
44///
45/// ```compile_fail
46/// use a3s_code_core::capability::{CapabilityLease, Run, Turn};
47///
48/// fn needs_run(_lease: CapabilityLease<'_, Run>) {}
49/// fn wrong_scope(lease: CapabilityLease<'_, Turn>) {
50///     needs_run(lease);
51/// }
52/// ```
53#[must_use = "a capability lease borrows and pins one active scope"]
54pub struct CapabilityLease<'scope, K: ScopeKind> {
55    inner: &'scope ScopeInner,
56    _kind: PhantomData<K>,
57}
58
59impl<'scope, K: ScopeKind> CapabilityLease<'scope, K> {
60    pub(super) fn new(inner: &'scope ScopeInner) -> Self {
61        Self {
62            inner,
63            _kind: PhantomData,
64        }
65    }
66
67    pub fn scope_id(&self) -> &CapabilityScopeId {
68        self.inner.id()
69    }
70
71    pub const fn kind(&self) -> CapabilityScopeKind {
72        K::KIND
73    }
74
75    pub fn parent_id(&self) -> Option<&CapabilityScopeId> {
76        self.inner.parent_id()
77    }
78
79    pub fn catalog_generation(&self) -> CodeCatalogGeneration {
80        self.inner.set().generation()
81    }
82
83    pub fn catalog_digest(&self) -> &Sha256Digest {
84        self.inner.set().digest()
85    }
86
87    pub fn use_generation(&self) -> Option<&UseCapabilityGeneration> {
88        self.inner.use_generation()
89    }
90
91    pub fn ceiling(&self) -> &CapabilityCeiling {
92        self.inner.ceiling()
93    }
94
95    pub fn cancellation(&self) -> CancellationToken {
96        self.inner.supervisor_cancellation()
97    }
98
99    pub fn get(
100        &self,
101        id: &CapabilityId,
102    ) -> Result<Option<&'scope CapabilityDescriptor>, CapabilityScopeError> {
103        self.inner.ensure_active()?;
104        if !self.inner.ceiling().allows(id) {
105            return Ok(None);
106        }
107        Ok(self.inner.set().get(id))
108    }
109
110    pub fn contains(&self, id: &CapabilityId) -> Result<bool, CapabilityScopeError> {
111        self.get(id).map(|descriptor| descriptor.is_some())
112    }
113
114    pub fn iter(
115        &self,
116    ) -> Result<
117        impl Iterator<Item = (&'scope CapabilityId, &'scope CapabilityDescriptor)> + 'scope,
118        CapabilityScopeError,
119    > {
120        self.inner.ensure_active()?;
121        let ceiling = self.inner.ceiling();
122        Ok(self
123            .inner
124            .set()
125            .iter()
126            .filter(move |(id, _)| ceiling.allows(id)))
127    }
128}
129
130impl<K: ScopeKind> std::fmt::Debug for CapabilityLease<'_, K> {
131    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        formatter
133            .debug_struct("CapabilityLease")
134            .field("scope_id", &self.scope_id())
135            .field("kind", &K::KIND)
136            .field("catalog_digest", &self.catalog_digest())
137            .finish()
138    }
139}