Skip to main content

sim_kernel/env/
core.rs

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