Skip to main content

arkhe_kernel/state/
scope.rs

1//! `InstanceScope<'i>` — invariant-lifetime-branded handle to an instance.
2//!
3//! GhostCell pattern (A19): the lifetime `'i` is the type-level brand
4//! preventing `Effect<Authorized, 'i>` of one instance from being passed
5//! to another instance's dispatcher. Production scopes are issued by an
6//! HRTB-bounded `Kernel::with_instance<F>(F: for<'i> ...)` (reserved
7//! / deferred); this module provides the type shape used today via the
8//! skeleton constructor below.
9
10use crate::abi::InstanceId;
11use crate::state::authz::InvariantLifetime;
12use core::marker::PhantomData;
13
14/// Invariant-lifetime-branded handle to an instance (GhostCell pattern,
15/// A19). The lifetime `'i` prevents an `Effect<Authorized, 'i>` of one
16/// instance from being passed to another instance's dispatcher —
17/// the mismatch fails lifetime unification at compile time.
18pub struct InstanceScope<'i> {
19    pub(crate) instance_id: InstanceId,
20    _brand: InvariantLifetime<'i>,
21}
22
23impl<'i> InstanceScope<'i> {
24    /// Test/skeleton constructor — production path is the deferred
25    /// HRTB-bounded `Kernel::with_instance`.
26    #[doc(hidden)]
27    pub fn __new_for_skeleton(instance_id: InstanceId) -> Self {
28        Self {
29            instance_id,
30            _brand: PhantomData,
31        }
32    }
33
34    /// `InstanceId` this scope is branded against.
35    pub fn instance_id(&self) -> InstanceId {
36        self.instance_id
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn scope_carries_instance_id() {
46        let id = InstanceId::new(42).unwrap();
47        let scope: InstanceScope<'_> = InstanceScope::__new_for_skeleton(id);
48        assert_eq!(scope.instance_id().get(), 42);
49    }
50
51    #[test]
52    fn scope_independent_construction_compiles() {
53        // Two scopes constructed in sequence with default elision:
54        // each gets its own lifetime; nothing flows between them.
55        let s1 = InstanceScope::__new_for_skeleton(InstanceId::new(1).unwrap());
56        let s2 = InstanceScope::__new_for_skeleton(InstanceId::new(2).unwrap());
57        assert_eq!(s1.instance_id().get(), 1);
58        assert_eq!(s2.instance_id().get(), 2);
59        // Cross-scope rejection at the dispatcher boundary becomes
60        // type-checkable when the HRTB scope guard ships (deferred).
61    }
62}