Skip to main content

sim_kernel/env/
core.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicU64, Ordering},
4};
5
6use crate::{
7    ContentId,
8    capability::{
9        CapabilityName, CapabilitySet, GrantSeat, macro_expansion_capability_for_phase,
10        read_construct_capability,
11    },
12    control::{ControlPolicy, ControlPolicyRef, NoopControlPolicy},
13    datum_store::BTreeDatumStore,
14    effect_ledger::EffectLedger,
15    error::{Diagnostic, Error, Result},
16    eval::{EvalPolicy, EvalPolicyRef, MacroExpanderRef, Phase},
17    expr::{Expr, SourceRegistry},
18    fact_store::{BTreeFactStore, FactStore},
19    factory::Factory,
20    handle_store::BTreeHandleStore,
21    id::{LibId, Symbol},
22    library::{LoadCx, Registry},
23    list::ListRegistry,
24    number_domain::PromotionSearchLimits,
25    object::Args,
26    table::TableRegistry,
27    value::Value,
28};
29
30use super::{Diagnostics, Env, load_ledger::LibLoadLedger};
31
32/// Monotonic source of per-context grant ids. Only used to bind a [`GrantSeat`]
33/// to the context it was minted with; the value is never observable in output,
34/// so it does not affect evaluation determinism.
35static NEXT_GRANT_ID: AtomicU64 = AtomicU64::new(1);
36
37fn unknown_symbol(symbol: &Symbol) -> Error {
38    Error::UnknownSymbol {
39        symbol: symbol.clone(),
40    }
41}
42
43fn class_protocol(value: &Value) -> Result<&dyn crate::Class> {
44    value.object().as_class().ok_or(Error::TypeMismatch {
45        expected: "class",
46        found: "non-class",
47    })
48}
49
50fn read_constructor_protocol(value: &Value) -> Result<&dyn crate::ReadConstructor> {
51    value
52        .object()
53        .as_read_constructor()
54        .ok_or(Error::TypeMismatch {
55            expected: "read-constructor",
56            found: "non-read-constructor",
57        })
58}
59
60/// The capability state of a [`Cx`]; an alias for [`CapabilitySet`].
61pub type Capabilities = CapabilitySet;
62
63/// The evaluation context threaded through every checked call.
64///
65/// `Cx` bundles the registry handle, factory, capability set, eval and control
66/// policies, the data substrate (datum/fact/handle stores and ledgers), and the
67/// diagnostic sink. The kernel defines this context; libraries supply the
68/// behavior reached through it (registered classes, functions, number domains,
69/// list/table backends, and so on). See the README sections "Library system"
70/// and "Capabilities and trust".
71pub struct Cx {
72    /// Per-context identity that binds a [`GrantSeat`] to exactly this context.
73    /// Minted at construction; a seat can only grant into the context it shares
74    /// this id with, so minting a fresh `(Cx, GrantSeat)` pair cannot be used to
75    /// escalate a different context.
76    grant_id: u64,
77    env: Env,
78    diagnostics: Diagnostics,
79    capabilities: Capabilities,
80    eval_policy: EvalPolicyRef,
81    macro_expander: Option<MacroExpanderRef>,
82    factory: Arc<dyn Factory>,
83    pub(crate) registry: Registry,
84    list_registry: ListRegistry,
85    table_registry: TableRegistry,
86    promotion_search_limits: PromotionSearchLimits,
87    sources: SourceRegistry,
88    datum_store: BTreeDatumStore,
89    handles: BTreeHandleStore,
90    facts: BTreeFactStore,
91    lib_load_ledger: LibLoadLedger,
92    effect_ledger: EffectLedger,
93    control_policy: ControlPolicyRef,
94}
95
96impl Cx {
97    /// Builds a fresh context with the given eval policy and factory.
98    ///
99    /// The registry, capability set, and stores start empty (boot claims aside);
100    /// libraries register behavior into the returned context.
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// # use std::sync::Arc;
106    /// # use sim_kernel::{Cx, DefaultFactory, NoopEvalPolicy};
107    /// let cx = Cx::new(Arc::new(NoopEvalPolicy), Arc::new(DefaultFactory));
108    /// assert!(cx.capabilities().iter().next().is_none());
109    /// ```
110    pub fn new(eval_policy: EvalPolicyRef, factory: Arc<dyn Factory>) -> Self {
111        let mut datum_store = BTreeDatumStore::default();
112        let mut facts = BTreeFactStore::default();
113        facts.insert_boot_claims(&mut datum_store);
114
115        Self {
116            grant_id: NEXT_GRANT_ID.fetch_add(1, Ordering::Relaxed),
117            env: Env::default(),
118            diagnostics: Diagnostics::default(),
119            capabilities: Capabilities::default(),
120            eval_policy,
121            macro_expander: None,
122            factory,
123            registry: Registry::default(),
124            list_registry: ListRegistry::default(),
125            table_registry: TableRegistry::default(),
126            promotion_search_limits: PromotionSearchLimits::default(),
127            sources: SourceRegistry::default(),
128            datum_store,
129            handles: BTreeHandleStore::default(),
130            facts,
131            lib_load_ledger: LibLoadLedger::default(),
132            effect_ledger: EffectLedger::default(),
133            control_policy: Arc::new(NoopControlPolicy),
134        }
135    }
136
137    /// Returns the active lexical environment.
138    pub fn env(&self) -> &Env {
139        &self.env
140    }
141
142    /// Returns the active lexical environment mutably.
143    pub fn env_mut(&mut self) -> &mut Env {
144        &mut self.env
145    }
146
147    /// Runs `f` with `env` installed as the active environment, then restores it.
148    pub fn with_env<T>(&mut self, env: Env, f: impl FnOnce(&mut Self) -> Result<T>) -> Result<T> {
149        let saved = std::mem::replace(&mut self.env, env);
150        let result = f(self);
151        self.env = saved;
152        result
153    }
154
155    /// Returns the active object [`Factory`].
156    pub fn factory(&self) -> &dyn Factory {
157        self.factory.as_ref()
158    }
159
160    /// Returns a shared handle to the active object factory.
161    pub fn factory_ref(&self) -> Arc<dyn Factory> {
162        self.factory.clone()
163    }
164
165    /// Runs `f` with `factory` installed as the active factory, then restores it.
166    pub fn with_factory<T>(
167        &mut self,
168        factory: Arc<dyn Factory>,
169        f: impl FnOnce(&mut Self) -> Result<T>,
170    ) -> Result<T> {
171        let saved = std::mem::replace(&mut self.factory, factory);
172        let result = f(self);
173        self.factory = saved;
174        result
175    }
176
177    /// Returns the behavior [`Registry`].
178    pub fn registry(&self) -> &Registry {
179        &self.registry
180    }
181
182    /// Returns the behavior registry mutably, for registering exports.
183    pub fn registry_mut(&mut self) -> &mut Registry {
184        &mut self.registry
185    }
186
187    /// Returns the registered list backend.
188    pub fn list_registry(&self) -> &ListRegistry {
189        &self.list_registry
190    }
191
192    /// Returns the list backend mutably.
193    pub fn list_registry_mut(&mut self) -> &mut ListRegistry {
194        &mut self.list_registry
195    }
196
197    /// Returns the registered table backend.
198    pub fn table_registry(&self) -> &TableRegistry {
199        &self.table_registry
200    }
201
202    /// Returns the table backend mutably.
203    pub fn table_registry_mut(&mut self) -> &mut TableRegistry {
204        &mut self.table_registry
205    }
206
207    /// Runs `f` with `registry` installed as the active registry, then restores it.
208    pub fn with_registry<T>(
209        &mut self,
210        registry: Registry,
211        f: impl FnOnce(&mut Self) -> Result<T>,
212    ) -> Result<T> {
213        let saved = std::mem::replace(&mut self.registry, registry);
214        let result = f(self);
215        self.registry = saved;
216        result
217    }
218
219    /// Builds a list value through the registered list backend.
220    pub fn new_list(&mut self, items: Vec<Value>) -> Result<Value> {
221        let registry = std::mem::take(&mut self.list_registry);
222        let result = registry.new_list(self, items);
223        self.list_registry = registry;
224        result
225    }
226
227    /// Builds a cons cell through the registered list backend.
228    pub fn new_cons(&mut self, car: Value, cdr: Value) -> Result<Value> {
229        let registry = std::mem::take(&mut self.list_registry);
230        let result = registry.new_cons(self, car, cdr);
231        self.list_registry = registry;
232        result
233    }
234
235    /// Builds a table value through the registered table backend.
236    pub fn new_table(&mut self, entries: Vec<(Symbol, Value)>) -> Result<Value> {
237        let registry = std::mem::take(&mut self.table_registry);
238        let result = registry.new_table(self, entries);
239        self.table_registry = registry;
240        result
241    }
242
243    /// Returns the source registry.
244    pub fn sources(&self) -> &SourceRegistry {
245        &self.sources
246    }
247
248    /// Returns the source registry mutably.
249    pub fn sources_mut(&mut self) -> &mut SourceRegistry {
250        &mut self.sources
251    }
252
253    /// Returns the datum store.
254    pub fn datum_store(&self) -> &BTreeDatumStore {
255        &self.datum_store
256    }
257
258    /// Returns the datum store mutably.
259    pub fn datum_store_mut(&mut self) -> &mut BTreeDatumStore {
260        &mut self.datum_store
261    }
262
263    /// Returns the handle store.
264    pub fn handles(&self) -> &BTreeHandleStore {
265        &self.handles
266    }
267
268    /// Returns the handle store mutably.
269    pub fn handles_mut(&mut self) -> &mut BTreeHandleStore {
270        &mut self.handles
271    }
272
273    /// Returns the fact store.
274    pub fn facts(&self) -> &BTreeFactStore {
275        &self.facts
276    }
277
278    /// Returns the fact store mutably.
279    pub fn facts_mut(&mut self) -> &mut BTreeFactStore {
280        &mut self.facts
281    }
282
283    /// Returns the effect ledger.
284    pub fn effect_ledger(&self) -> &EffectLedger {
285        &self.effect_ledger
286    }
287
288    /// Returns the effect ledger mutably.
289    pub fn effect_ledger_mut(&mut self) -> &mut EffectLedger {
290        &mut self.effect_ledger
291    }
292
293    /// Returns the active control policy.
294    pub fn control_policy(&self) -> &dyn ControlPolicy {
295        self.control_policy.as_ref()
296    }
297
298    /// Returns a shared handle to the active control policy.
299    pub fn control_policy_ref(&self) -> ControlPolicyRef {
300        self.control_policy.clone()
301    }
302
303    /// Returns the name of the active control policy.
304    pub fn control_policy_name(&self) -> &'static str {
305        self.control_policy.name()
306    }
307
308    /// Replaces the active control policy.
309    pub fn set_control_policy(&mut self, control_policy: ControlPolicyRef) {
310        self.control_policy = control_policy;
311    }
312
313    pub(crate) fn with_effect_ledger<T>(
314        &mut self,
315        f: impl FnOnce(&mut Self, &mut EffectLedger) -> Result<T>,
316    ) -> Result<T> {
317        let mut ledger = std::mem::take(&mut self.effect_ledger);
318        let result = f(self, &mut ledger);
319        self.effect_ledger = ledger;
320        result
321    }
322
323    /// Inserts a claim into the fact store, subject to capability authorization.
324    pub fn insert_fact(&mut self, claim: crate::Claim) -> Result<crate::Ref> {
325        self.facts
326            .insert_authorized(&self.capabilities, &mut self.datum_store, claim)
327    }
328
329    /// Inserts a claim and records it as part of a loaded library's receipt.
330    ///
331    /// When the claim did not already exist, `unload_lib` retracts it with the
332    /// rest of `lib_id`'s recorded load effects. Pre-existing identical claims
333    /// are left owned by their original publisher.
334    pub fn insert_fact_for_lib(
335        &mut self,
336        lib_id: LibId,
337        claim: crate::Claim,
338    ) -> Result<crate::Ref> {
339        let (reference, inserted) = self.insert_recorded_fact(claim)?;
340        if let Some(inserted) = inserted {
341            self.record_load_claims(lib_id, vec![inserted]);
342        }
343        Ok(reference)
344    }
345
346    pub(crate) fn insert_recorded_fact(
347        &mut self,
348        claim: crate::Claim,
349    ) -> Result<(crate::Ref, Option<ContentId>)> {
350        let id = claim.content_id(&mut self.datum_store)?;
351        let existed = self.facts.get(&id).is_some();
352        let inserted = self.insert_fact(claim)?;
353        let crate::Ref::Content(inserted_id) = inserted else {
354            return Err(Error::Lib(
355                "fact insertion returned a non-content reference".to_owned(),
356            ));
357        };
358        debug_assert_eq!(inserted_id, id);
359        Ok((
360            crate::Ref::Content(inserted_id.clone()),
361            (!existed).then_some(inserted_id),
362        ))
363    }
364
365    /// Queries the fact store for claims matching `pattern`, applying read policy.
366    pub fn query_facts(&self, pattern: crate::ClaimPattern) -> Result<Vec<crate::Claim>> {
367        self.facts.query_authorized(self, pattern)
368    }
369
370    pub(crate) fn record_load_claims(&mut self, lib_id: LibId, claim_ids: Vec<ContentId>) {
371        self.lib_load_ledger.record_claims(lib_id, claim_ids);
372    }
373
374    pub(crate) fn remove_load_claims(&mut self, lib_ids: &[LibId]) {
375        self.lib_load_ledger.remove_claims(lib_ids, &mut self.facts);
376    }
377
378    /// Returns the limits bounding number-domain promotion search.
379    pub fn promotion_search_limits(&self) -> PromotionSearchLimits {
380        self.promotion_search_limits
381    }
382
383    /// Sets the limits bounding number-domain promotion search.
384    pub fn set_promotion_search_limits(&mut self, limits: PromotionSearchLimits) {
385        self.promotion_search_limits = limits;
386    }
387
388    pub(crate) fn load_cx(&self) -> LoadCx {
389        LoadCx::new(
390            self.capabilities.clone(),
391            self.factory.clone(),
392            self.registry.clone(),
393        )
394    }
395
396    /// Returns the active evaluation policy.
397    pub fn eval_policy(&self) -> &dyn EvalPolicy {
398        self.eval_policy.as_ref()
399    }
400
401    /// Returns a shared handle to the active evaluation policy.
402    pub fn eval_policy_ref(&self) -> EvalPolicyRef {
403        self.eval_policy.clone()
404    }
405
406    /// Returns the name of the active evaluation policy.
407    pub fn eval_policy_name(&self) -> &'static str {
408        self.eval_policy.name()
409    }
410
411    /// Replaces the active evaluation policy.
412    pub fn set_eval_policy(&mut self, eval_policy: EvalPolicyRef) {
413        self.eval_policy = eval_policy;
414    }
415
416    /// Installs a macro expander.
417    pub fn set_macro_expander(&mut self, macro_expander: MacroExpanderRef) {
418        self.macro_expander = Some(macro_expander);
419    }
420
421    /// Removes any installed macro expander.
422    pub fn clear_macro_expander(&mut self) {
423        self.macro_expander = None;
424    }
425
426    /// Returns the installed macro expander, if any.
427    pub fn macro_expander_ref(&self) -> Option<MacroExpanderRef> {
428        self.macro_expander.clone()
429    }
430
431    /// Expands macros in `expr` for the given phase, or returns it unchanged.
432    pub fn expand_macros(&mut self, phase: Phase, expr: Expr) -> Result<Expr> {
433        let Some(expander) = self.macro_expander.clone() else {
434            return Ok(expr);
435        };
436        let eval_policy = self.eval_policy_ref();
437        if !eval_policy.allow_macro_expansion(phase) {
438            return Err(Error::Eval(format!(
439                "macro expansion denied by eval policy {} for {phase:?}",
440                eval_policy.name()
441            )));
442        }
443        self.require(&macro_expansion_capability_for_phase(phase))?;
444        expander.expand_expr(self, phase, expr)
445    }
446
447    /// Returns the accumulated diagnostics.
448    pub fn diagnostics(&self) -> &Diagnostics {
449        &self.diagnostics
450    }
451
452    /// Drains and returns the accumulated diagnostics.
453    pub fn take_diagnostics(&mut self) -> Vec<Diagnostic> {
454        self.diagnostics.take()
455    }
456
457    /// Records an already-built diagnostic.
458    pub fn push_diagnostic(&mut self, diagnostic: Diagnostic) {
459        self.diagnostics.push_diagnostic(diagnostic);
460    }
461
462    /// Records an info-level diagnostic from a message.
463    pub fn push_info(&mut self, message: impl Into<String>) {
464        self.diagnostics.push_info(message);
465    }
466
467    /// Constructs a context together with its host [`GrantSeat`].
468    ///
469    /// The caller (a bootloader, a server session, a loader) keeps the seat and
470    /// grants capabilities through it. The seat is never handed to a callable, so
471    /// loaded behavior cannot grant itself a capability. Use this instead of
472    /// [`Cx::new`] wherever the host needs to grant capabilities.
473    pub fn new_seated(eval_policy: EvalPolicyRef, factory: Arc<dyn Factory>) -> (Self, GrantSeat) {
474        let cx = Self::new(eval_policy, factory);
475        let seat = GrantSeat::for_cx(cx.grant_id);
476        (cx, seat)
477    }
478
479    /// The identity that binds a [`GrantSeat`] to this context.
480    pub(crate) fn grant_id(&self) -> u64 {
481        self.grant_id
482    }
483
484    /// Grants a capability into this context under host authority.
485    ///
486    /// This is the single internal insert point; the only ways to reach it are a
487    /// host-held [`GrantSeat`] (production) or the `test-support` grant methods
488    /// (tests). A loaded callable holds a `&mut Cx` but no seat, so it cannot
489    /// grant itself a capability.
490    pub(crate) fn grant_from_host(&mut self, capability: CapabilityName) {
491        self.capabilities.insert(capability);
492    }
493
494    /// Grants a capability to this context. TEST-ONLY: available only under the
495    /// `test-support` feature. Production code grants through a host [`GrantSeat`]
496    /// ([`Cx::new_seated`]); a loaded callable has no grant path at all.
497    #[cfg(feature = "test-support")]
498    pub fn grant(&mut self, capability: CapabilityName) {
499        self.grant_from_host(capability);
500    }
501
502    /// Grants a capability named by a static string. TEST-ONLY: available only
503    /// under the `test-support` feature.
504    #[cfg(feature = "test-support")]
505    pub fn grant_named(&mut self, capability: &'static str) {
506        self.grant_from_host(CapabilityName::new(capability));
507    }
508
509    /// Returns the granted capability set.
510    pub fn capabilities(&self) -> &Capabilities {
511        &self.capabilities
512    }
513
514    /// Runs `f` with `capabilities` installed, then restores the prior set.
515    pub fn with_capabilities<T>(
516        &mut self,
517        capabilities: Capabilities,
518        f: impl FnOnce(&mut Self) -> Result<T>,
519    ) -> Result<T> {
520        let saved = std::mem::replace(&mut self.capabilities, capabilities);
521        let result = f(self);
522        self.capabilities = saved;
523        result
524    }
525
526    /// Resolves a registered class by symbol.
527    pub fn resolve_class(&self, symbol: &Symbol) -> Result<Value> {
528        self.resolve_registered_value(
529            |registry| registry.class_by_symbol(symbol),
530            Error::UnknownClass {
531                class: symbol.clone(),
532            },
533        )
534    }
535
536    /// Resolves a registered function by symbol.
537    pub fn resolve_function(&self, symbol: &Symbol) -> Result<Value> {
538        self.resolve_registered_value(
539            |registry| registry.function_by_symbol(symbol),
540            Error::UnknownFunction {
541                function: symbol.clone(),
542            },
543        )
544    }
545
546    /// Resolves a registered macro by symbol.
547    pub fn resolve_macro(&self, symbol: &Symbol) -> Result<Value> {
548        self.resolve_registered_value(
549            |registry| registry.macro_by_symbol(symbol),
550            unknown_symbol(symbol),
551        )
552    }
553
554    /// Resolves a registered shape by symbol.
555    pub fn resolve_shape(&self, symbol: &Symbol) -> Result<Value> {
556        self.resolve_registered_value(
557            |registry| registry.shape_by_symbol(symbol),
558            unknown_symbol(symbol),
559        )
560    }
561
562    /// Resolves a registered codec by symbol.
563    pub fn resolve_codec(&self, symbol: &Symbol) -> Result<Value> {
564        self.resolve_registered_value(
565            |registry| registry.codec_by_symbol(symbol),
566            unknown_symbol(symbol),
567        )
568    }
569
570    /// Resolves a registered number domain by symbol.
571    pub fn resolve_number_domain(&self, symbol: &Symbol) -> Result<Value> {
572        self.resolve_registered_value(
573            |registry| registry.number_domain_by_symbol(symbol),
574            unknown_symbol(symbol),
575        )
576    }
577
578    /// Resolves a registered value binding by symbol.
579    pub fn resolve_value(&self, symbol: &Symbol) -> Result<Value> {
580        self.resolve_registered_value(
581            |registry| registry.value_by_symbol(symbol),
582            unknown_symbol(symbol),
583        )
584    }
585
586    fn resolve_registered_value(
587        &self,
588        lookup: impl FnOnce(&Registry) -> Option<&Value>,
589        not_found: Error,
590    ) -> Result<Value> {
591        lookup(self.registry()).cloned().ok_or(not_found)
592    }
593
594    /// Calls a callable value with already-evaluated arguments.
595    pub fn call_value(&mut self, value: Value, args: Args) -> Result<Value> {
596        let Some(callable) = value.object().as_callable() else {
597            return Err(Error::TypeMismatch {
598                expected: "callable",
599                found: "non-callable",
600            });
601        };
602        callable.call(self, args)
603    }
604
605    /// Calls a callable value with raw, unevaluated argument expressions.
606    pub fn call_exprs(&mut self, value: Value, args: Vec<crate::expr::Expr>) -> Result<Value> {
607        let Some(callable) = value.object().as_callable() else {
608            return Err(Error::TypeMismatch {
609                expected: "callable",
610                found: "non-callable",
611            });
612        };
613        callable.call_exprs(self, crate::object::RawArgs::new(args))
614    }
615
616    /// Resolves a function by symbol and calls it.
617    pub fn call_function(&mut self, symbol: &Symbol, args: Args) -> Result<Value> {
618        let function = self.resolve_function(symbol)?;
619        self.call_value(function, args)
620    }
621
622    /// Resolves a class by symbol and calls its constructor.
623    pub fn call_class(&mut self, symbol: &Symbol, args: Args) -> Result<Value> {
624        let class = self.resolve_class(symbol)?;
625        self.call_value(class, args)
626    }
627
628    /// Constructs an instance of `class` from read-time arguments.
629    ///
630    /// Requires the [`read_construct_capability`](crate::capability::read_construct_capability).
631    pub fn read_construct(&mut self, class: &Symbol, args: Vec<Value>) -> Result<Value> {
632        self.require(&read_construct_capability())?;
633        let class_value = self.resolve_class(class)?;
634        let constructor = class_protocol(&class_value)?
635            .read_constructor(self)?
636            .ok_or_else(|| Error::Eval(format!("class {class} has no read constructor")))?;
637        read_constructor_protocol(&constructor)?.construct_read(self, args)
638    }
639
640    /// Forces a value to the requested demand through the active eval policy.
641    pub fn force(&mut self, value: Value, demand: crate::eval::Demand) -> Result<Value> {
642        let eval_policy = self.eval_policy.clone();
643        eval_policy.force(self, value, demand)
644    }
645
646    /// Evaluates an expression through the active eval policy.
647    pub fn eval_expr(&mut self, expr: crate::expr::Expr) -> Result<Value> {
648        let eval_policy = self.eval_policy.clone();
649        eval_policy.eval_expr(self, expr)
650    }
651
652    /// Returns true when `name` resolves across environment or registry layers.
653    pub fn symbol_is_bound(&mut self, name: &Symbol) -> bool {
654        self.env().get(name).is_some()
655            || self.resolve_function(name).is_ok()
656            || self.resolve_class(name).is_ok()
657            || self.resolve_shape(name).is_ok()
658            || self.resolve_value(name).is_ok()
659    }
660
661    /// Resolves an unbound value-position symbol through the active eval policy.
662    pub fn resolve_unbound_symbol(&mut self, symbol: Symbol) -> Result<Value> {
663        let eval_policy = self.eval_policy_ref();
664        eval_policy.resolve_unbound_symbol(self, symbol)
665    }
666
667    /// Resolves an unbound call through the active eval policy.
668    pub fn resolve_unbound_call(
669        &mut self,
670        operator: Symbol,
671        args: Vec<crate::expr::Expr>,
672    ) -> Result<Value> {
673        let eval_policy = self.eval_policy_ref();
674        eval_policy.resolve_unbound_call(self, operator, args)
675    }
676
677    /// Demands a capability, returning [`Error::CapabilityDenied`] when absent.
678    pub fn require(&self, capability: &CapabilityName) -> Result<()> {
679        if self.capabilities.contains(capability) {
680            Ok(())
681        } else {
682            Err(Error::CapabilityDenied {
683                capability: capability.clone(),
684            })
685        }
686    }
687
688    /// Demands every capability in turn, failing on the first absent one.
689    pub fn require_all(&self, capabilities: &[CapabilityName]) -> Result<()> {
690        for capability in capabilities {
691            self.require(capability)?;
692        }
693        Ok(())
694    }
695}