Skip to main content

cml/types/
capability.rs

1// Copyright 2025 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::{
6    AnyRef, AsClause, AsClauseContext, Canonicalize, CanonicalizeContext, CapabilityClause,
7    ConfigNestedValueType, ConfigType, Error, FilterClause, PathClause,
8};
9
10use crate::one_or_many::{OneOrMany, always_one, always_one_context, option_one_or_many_as_ref};
11use crate::types::common::*;
12use crate::types::right::{Rights, RightsClause};
13pub use cm_types::{
14    Availability, BorrowedName, BoundedName, DeliveryType, DependencyType, HandleType, Name,
15    OnTerminate, ParseError, Path, RelativePath, StartupMode, StorageId, Url,
16};
17use cml_macro::Reference;
18use reference_doc::ReferenceDoc;
19use serde::{Deserialize, Serialize};
20use serde_json::{Map, Value};
21use std::num::NonZeroU32;
22
23use std::fmt;
24use std::path::PathBuf;
25use std::sync::Arc;
26
27#[derive(Deserialize, Debug, PartialEq, Clone, ReferenceDoc, Serialize, Default)]
28#[serde(deny_unknown_fields)]
29#[reference_doc(fields_as = "list")]
30pub struct Capability {
31    /// The [name](#name) for this service capability. Specifying `path` is valid
32    /// only when this value is a string.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    #[reference_doc(skip = true)]
35    pub service: Option<OneOrMany<Name>>,
36
37    /// The [name](#name) for this protocol capability. Specifying `path` is valid
38    /// only when this value is a string.
39    #[serde(skip_serializing_if = "Option::is_none")]
40    #[reference_doc(skip = true)]
41    pub protocol: Option<OneOrMany<Name>>,
42
43    /// The [name](#name) for this directory capability.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    #[reference_doc(skip = true)]
46    pub directory: Option<Name>,
47
48    /// The [name](#name) for this storage capability.
49    #[serde(skip_serializing_if = "Option::is_none")]
50    #[reference_doc(skip = true)]
51    pub storage: Option<Name>,
52
53    /// The [name](#name) for this runner capability.
54    #[serde(skip_serializing_if = "Option::is_none")]
55    #[reference_doc(skip = true)]
56    pub runner: Option<Name>,
57
58    /// The [name](#name) for this resolver capability.
59    #[serde(skip_serializing_if = "Option::is_none")]
60    #[reference_doc(skip = true)]
61    pub resolver: Option<Name>,
62
63    /// The [name](#name) for this event_stream capability.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    #[reference_doc(skip = true)]
66    pub event_stream: Option<OneOrMany<Name>>,
67
68    /// The [name](#name) for this dictionary capability.
69    #[serde(skip_serializing_if = "Option::is_none")]
70    #[reference_doc(skip = true)]
71    pub dictionary: Option<Name>,
72
73    /// The [name](#name) for this configuration capability.
74    #[serde(skip_serializing_if = "Option::is_none")]
75    #[reference_doc(skip = true)]
76    pub config: Option<Name>,
77
78    /// The path within the [outgoing directory][glossary.outgoing directory] of the component's
79    /// program to source the capability.
80    ///
81    /// For `protocol` and `service`, defaults to `/svc/${protocol}`, otherwise required.
82    ///
83    /// For `protocol`, the target of the path MUST be a channel, which tends to speak
84    /// the protocol matching the name of this capability.
85    ///
86    /// For `service`, `directory`, the target of the path MUST be a directory.
87    ///
88    /// For `runner`, the target of the path MUST be a channel and MUST speak
89    /// the protocol `fuchsia.component.runner.ComponentRunner`.
90    ///
91    /// For `resolver`, the target of the path MUST be a channel and MUST speak
92    /// the protocol `fuchsia.component.resolution.Resolver`.
93    ///
94    /// For `dictionary`, this is optional. If provided, it is a path to a
95    /// `fuchsia.component.sandbox/DictionaryRouter` served by the program which should return a
96    /// `fuchsia.component.sandbox/DictionaryRef`, by which the program may dynamically provide
97    /// a dictionary from itself. If this is set for `dictionary`, `offer` to this dictionary
98    /// is not allowed.
99    #[serde(skip_serializing_if = "Option::is_none")]
100    pub path: Option<Path>,
101
102    /// (`directory` only) The maximum [directory rights][doc-directory-rights] that may be set
103    /// when using this directory.
104    #[serde(skip_serializing_if = "Option::is_none")]
105    #[reference_doc(json_type = "array of string")]
106    pub rights: Option<Rights>,
107
108    /// (`storage` only) The source component of an existing directory capability backing this
109    /// storage capability, one of:
110    /// - `parent`: The component's parent.
111    /// - `self`: This component.
112    /// - `#<child-name>`: A [reference](#references) to a child component
113    ///     instance.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub from: Option<CapabilityFromRef>,
116
117    /// (`storage` only) The [name](#name) of the directory capability backing the storage. The
118    /// capability must be available from the component referenced in `from`.
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub backing_dir: Option<Name>,
121
122    /// (`storage` only) A subdirectory within `backing_dir` where per-component isolated storage
123    /// directories are created
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub subdir: Option<RelativePath>,
126
127    /// (`storage` only) The identifier used to isolated storage for a component, one of:
128    /// - `static_instance_id`: The instance ID in the component ID index is used
129    ///     as the key for a component's storage. Components which are not listed in
130    ///     the component ID index will not be able to use this storage capability.
131    /// - `static_instance_id_or_moniker`: If the component is listed in the
132    ///     component ID index, the instance ID is used as the key for a component's
133    ///     storage. Otherwise, the component's moniker from the storage
134    ///     capability is used.
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub storage_id: Option<StorageId>,
137
138    /// (`configuration` only) The type of configuration, one of:
139    /// - `bool`: Boolean type.
140    /// - `uint8`: Unsigned 8 bit type.
141    /// - `uint16`: Unsigned 16 bit type.
142    /// - `uint32`: Unsigned 32 bit type.
143    /// - `uint64`: Unsigned 64 bit type.
144    /// - `int8`: Signed 8 bit type.
145    /// - `int16`: Signed 16 bit type.
146    /// - `int32`: Signed 32 bit type.
147    /// - `int64`: Signed 64 bit type.
148    /// - `string`: ASCII string type.
149    /// - `vector`: Vector type. See `element` for the type of the element within the vector.
150    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
151    #[reference_doc(rename = "type")]
152    pub config_type: Option<ConfigType>,
153
154    /// (`configuration` only) Only supported if this configuration `type` is 'string'.
155    /// This is the max size of the string.
156    #[serde(rename = "max_size", skip_serializing_if = "Option::is_none")]
157    #[reference_doc(rename = "max_size")]
158    pub config_max_size: Option<NonZeroU32>,
159
160    /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
161    /// This is the max number of elements in the vector.
162    #[serde(rename = "max_count", skip_serializing_if = "Option::is_none")]
163    #[reference_doc(rename = "max_count")]
164    pub config_max_count: Option<NonZeroU32>,
165
166    /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
167    /// This is the type of the elements in the configuration vector.
168    ///
169    /// Example (simple type):
170    ///
171    /// ```json5
172    /// { type: "uint8" }
173    /// ```
174    ///
175    /// Example (string type):
176    ///
177    /// ```json5
178    /// {
179    ///   type: "string",
180    ///   max_size: 100,
181    /// }
182    /// ```
183    #[serde(rename = "element", skip_serializing_if = "Option::is_none")]
184    #[reference_doc(rename = "element", json_type = "object")]
185    pub config_element_type: Option<ConfigNestedValueType>,
186
187    /// (`configuration` only) The value of the configuration.
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub value: Option<serde_json::Value>,
190
191    /// (`protocol` only) Specifies when the framework will open the protocol
192    /// from this component's outgoing directory when someone requests the
193    /// capability. Allowed values are:
194    ///
195    /// - `eager`: (default) the framework will open the capability as soon as
196    ///   some consumer component requests it.
197    /// - `on_readable`: the framework will open the capability when the server
198    ///   endpoint pipelined in a connection request becomes readable.
199    ///
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub delivery: Option<DeliveryType>,
202}
203
204impl Canonicalize for Capability {
205    fn canonicalize(&mut self) {
206        // Sort the names of the capabilities. Only capabilities with OneOrMany values are included here.
207        if let Some(service) = &mut self.service {
208            service.canonicalize()
209        } else if let Some(protocol) = &mut self.protocol {
210            protocol.canonicalize()
211        } else if let Some(event_stream) = &mut self.event_stream {
212            event_stream.canonicalize()
213        }
214    }
215}
216
217impl AsClause for Capability {
218    fn r#as(&self) -> Option<&BorrowedName> {
219        None
220    }
221}
222
223impl PathClause for Capability {
224    fn path(&self) -> Option<&Path> {
225        self.path.as_ref()
226    }
227}
228
229impl FilterClause for Capability {
230    fn filter(&self) -> Option<&Map<String, Value>> {
231        None
232    }
233}
234
235impl RightsClause for Capability {
236    fn rights(&self) -> Option<&Rights> {
237        self.rights.as_ref()
238    }
239}
240
241impl CapabilityClause for Capability {
242    fn service(&self) -> Option<OneOrMany<&BorrowedName>> {
243        option_one_or_many_as_ref(&self.service)
244    }
245    fn protocol(&self) -> Option<OneOrMany<&BorrowedName>> {
246        option_one_or_many_as_ref(&self.protocol)
247    }
248    fn directory(&self) -> Option<OneOrMany<&BorrowedName>> {
249        self.directory.as_ref().map(|n| OneOrMany::One(n.as_ref()))
250    }
251    fn storage(&self) -> Option<OneOrMany<&BorrowedName>> {
252        self.storage.as_ref().map(|n| OneOrMany::One(n.as_ref()))
253    }
254    fn runner(&self) -> Option<OneOrMany<&BorrowedName>> {
255        self.runner.as_ref().map(|n| OneOrMany::One(n.as_ref()))
256    }
257    fn resolver(&self) -> Option<OneOrMany<&BorrowedName>> {
258        self.resolver.as_ref().map(|n| OneOrMany::One(n.as_ref()))
259    }
260    fn event_stream(&self) -> Option<OneOrMany<&BorrowedName>> {
261        option_one_or_many_as_ref(&self.event_stream)
262    }
263    fn dictionary(&self) -> Option<OneOrMany<&BorrowedName>> {
264        self.dictionary.as_ref().map(|n| OneOrMany::One(n.as_ref()))
265    }
266    fn config(&self) -> Option<OneOrMany<&BorrowedName>> {
267        self.config.as_ref().map(|n| OneOrMany::One(n.as_ref()))
268    }
269
270    fn set_service(&mut self, o: Option<OneOrMany<Name>>) {
271        self.service = o;
272    }
273    fn set_protocol(&mut self, o: Option<OneOrMany<Name>>) {
274        self.protocol = o;
275    }
276    fn set_directory(&mut self, o: Option<OneOrMany<Name>>) {
277        self.directory = always_one(o);
278    }
279    fn set_storage(&mut self, o: Option<OneOrMany<Name>>) {
280        self.storage = always_one(o);
281    }
282    fn set_runner(&mut self, o: Option<OneOrMany<Name>>) {
283        self.runner = always_one(o);
284    }
285    fn set_resolver(&mut self, o: Option<OneOrMany<Name>>) {
286        self.resolver = always_one(o);
287    }
288    fn set_event_stream(&mut self, o: Option<OneOrMany<Name>>) {
289        self.event_stream = o;
290    }
291    fn set_dictionary(&mut self, o: Option<OneOrMany<Name>>) {
292        self.dictionary = always_one(o);
293    }
294
295    fn set_config(&mut self, o: Option<OneOrMany<Name>>) {
296        self.config = always_one(o);
297    }
298
299    fn availability(&self) -> Option<Availability> {
300        None
301    }
302    fn set_availability(&mut self, _a: Option<Availability>) {}
303
304    fn decl_type(&self) -> &'static str {
305        "capability"
306    }
307    fn supported(&self) -> &[&'static str] {
308        &[
309            "service",
310            "protocol",
311            "directory",
312            "storage",
313            "runner",
314            "resolver",
315            "event_stream",
316            "dictionary",
317            "config",
318        ]
319    }
320    fn are_many_names_allowed(&self) -> bool {
321        ["service", "protocol", "event_stream"].contains(&self.capability_type().unwrap())
322    }
323}
324
325/// A reference in a `storage from`.
326#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
327#[reference(expected = "\"parent\", \"self\", or \"#<child-name>\"")]
328pub enum CapabilityFromRef {
329    /// A reference to a child.
330    Named(Name),
331    /// A reference to the parent.
332    Parent,
333    /// A reference to this component.
334    Self_,
335}
336
337#[derive(Debug, Clone, Serialize)]
338pub struct ContextCapability {
339    #[serde(skip)]
340    pub origin: Arc<PathBuf>,
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub service: Option<ContextSpanned<OneOrMany<Name>>>,
343    #[serde(skip_serializing_if = "Option::is_none")]
344    pub protocol: Option<ContextSpanned<OneOrMany<Name>>>,
345    #[serde(skip_serializing_if = "Option::is_none")]
346    pub directory: Option<ContextSpanned<Name>>,
347    #[serde(skip_serializing_if = "Option::is_none")]
348    pub storage: Option<ContextSpanned<Name>>,
349    #[serde(skip_serializing_if = "Option::is_none")]
350    pub runner: Option<ContextSpanned<Name>>,
351    #[serde(skip_serializing_if = "Option::is_none")]
352    pub resolver: Option<ContextSpanned<Name>>,
353    #[serde(skip_serializing_if = "Option::is_none")]
354    pub event_stream: Option<ContextSpanned<OneOrMany<Name>>>,
355    #[serde(skip_serializing_if = "Option::is_none")]
356    pub dictionary: Option<ContextSpanned<Name>>,
357    #[serde(skip_serializing_if = "Option::is_none")]
358    pub config: Option<ContextSpanned<Name>>,
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub path: Option<ContextSpanned<Path>>,
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub rights: Option<ContextSpanned<Rights>>,
363    #[serde(skip_serializing_if = "Option::is_none")]
364    pub from: Option<ContextSpanned<CapabilityFromRef>>,
365    #[serde(skip_serializing_if = "Option::is_none")]
366    pub backing_dir: Option<ContextSpanned<Name>>,
367    #[serde(skip_serializing_if = "Option::is_none")]
368    pub subdir: Option<ContextSpanned<RelativePath>>,
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub storage_id: Option<ContextSpanned<StorageId>>,
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub config_type: Option<ContextSpanned<ConfigType>>,
373    #[serde(skip_serializing_if = "Option::is_none")]
374    pub config_max_size: Option<ContextSpanned<NonZeroU32>>,
375    #[serde(skip_serializing_if = "Option::is_none")]
376    pub config_max_count: Option<ContextSpanned<NonZeroU32>>,
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub config_element_type: Option<ContextSpanned<ConfigNestedValueType>>,
379    #[serde(skip_serializing_if = "Option::is_none")]
380    pub value: Option<ContextSpanned<serde_json::Value>>,
381    #[serde(skip_serializing_if = "Option::is_none")]
382    pub delivery: Option<ContextSpanned<DeliveryType>>,
383}
384
385impl CanonicalizeContext for ContextCapability {
386    fn canonicalize_context(&mut self) {
387        // Sort the names of the capabilities. Only capabilities with OneOrMany values are included here.
388        if let Some(service) = &mut self.service {
389            service.value.canonicalize_context()
390        } else if let Some(protocol) = &mut self.protocol {
391            protocol.value.canonicalize_context()
392        } else if let Some(event_stream) = &mut self.event_stream {
393            event_stream.value.canonicalize_context()
394        }
395    }
396}
397
398impl RightsClause for ContextCapability {
399    fn rights(&self) -> Option<&Rights> {
400        self.rights.as_ref().map(|r| &r.value)
401    }
402}
403
404impl ContextCapabilityClause for ContextCapability {
405    fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
406        option_one_or_many_as_ref_context(&self.service)
407    }
408    fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
409        option_one_or_many_as_ref_context(&self.protocol)
410    }
411    fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
412        self.directory.as_ref().map(|s| ContextSpanned {
413            value: OneOrMany::One((s.value).as_ref()),
414            origin: s.origin.clone(),
415        })
416    }
417    fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
418        self.storage.as_ref().map(|s| ContextSpanned {
419            value: OneOrMany::One((s.value).as_ref()),
420            origin: s.origin.clone(),
421        })
422    }
423    fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
424        self.runner.as_ref().map(|s| ContextSpanned {
425            value: OneOrMany::One((s.value).as_ref()),
426            origin: s.origin.clone(),
427        })
428    }
429    fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
430        self.resolver.as_ref().map(|s| ContextSpanned {
431            value: OneOrMany::One((s.value).as_ref()),
432            origin: s.origin.clone(),
433        })
434    }
435    fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
436        option_one_or_many_as_ref_context(&self.event_stream)
437    }
438    fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
439        self.dictionary.as_ref().map(|s| ContextSpanned {
440            value: OneOrMany::One((s.value).as_ref()),
441            origin: s.origin.clone(),
442        })
443    }
444    fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
445        self.config.as_ref().map(|s| ContextSpanned {
446            value: OneOrMany::One((s.value).as_ref()),
447            origin: s.origin.clone(),
448        })
449    }
450
451    fn decl_type(&self) -> &'static str {
452        "capability"
453    }
454    fn supported(&self) -> &[&'static str] {
455        &[
456            "service",
457            "protocol",
458            "directory",
459            "storage",
460            "event_stream",
461            "runner",
462            "resolver",
463            "config",
464            "dicitionary",
465        ]
466    }
467    fn are_many_names_allowed(&self) -> bool {
468        [
469            "service",
470            "protocol",
471            "directory",
472            "runner",
473            "resolver",
474            "event_stream",
475            "config",
476            "dicitionary",
477        ]
478        .contains(&self.capability_type(None).unwrap())
479    }
480
481    fn set_service(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
482        self.service = o;
483    }
484    fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
485        self.protocol = o;
486    }
487    fn set_directory(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
488        self.directory = always_one_context(o);
489    }
490    fn set_storage(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
491        self.storage = always_one_context(o);
492    }
493    fn set_runner(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
494        self.runner = always_one_context(o);
495    }
496    fn set_resolver(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
497        self.resolver = always_one_context(o);
498    }
499    fn set_event_stream(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
500        self.event_stream = o;
501    }
502    fn set_dictionary(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
503        self.dictionary = always_one_context(o);
504    }
505    fn set_config(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
506        self.config = always_one_context(o);
507    }
508
509    /// Returns the origin of this capability.
510    fn origin(&self) -> &Arc<PathBuf> {
511        &self.origin
512    }
513
514    fn availability(&self) -> Option<ContextSpanned<Availability>> {
515        None
516    }
517    fn set_availability(&mut self, _a: Option<ContextSpanned<Availability>>) {}
518}
519
520impl PartialEq for ContextCapability {
521    fn eq(&self, other: &Self) -> bool {
522        macro_rules! cmp {
523            ($field:ident) => {
524                match (&self.$field, &other.$field) {
525                    (Some(a), Some(b)) => a.value == b.value,
526                    (None, None) => true,
527                    _ => false,
528                }
529            };
530        }
531
532        cmp!(service)
533            && cmp!(protocol)
534            && cmp!(directory)
535            && cmp!(storage)
536            && cmp!(runner)
537            && cmp!(resolver)
538            && cmp!(dictionary)
539            && cmp!(config)
540            && cmp!(path)
541            && cmp!(rights)
542            && cmp!(from)
543            && cmp!(event_stream)
544            && cmp!(backing_dir)
545            && cmp!(subdir)
546            && cmp!(storage_id)
547            && cmp!(config_type)
548            && cmp!(config_max_size)
549            && cmp!(config_max_count)
550            && cmp!(config_element_type)
551            && cmp!(value)
552            && cmp!(delivery)
553    }
554}
555
556impl Eq for ContextCapability {}
557
558impl ContextPathClause for ContextCapability {
559    fn path(&self) -> Option<&ContextSpanned<Path>> {
560        self.path.as_ref()
561    }
562}
563
564impl AsClauseContext for ContextCapability {
565    fn r#as(&self) -> Option<ContextSpanned<&BorrowedName>> {
566        None
567    }
568}
569
570impl Hydrate for Capability {
571    type Output = ContextCapability;
572
573    fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error> {
574        Ok(ContextCapability {
575            origin: file.clone(),
576            service: hydrate_opt_simple(self.service, file),
577            protocol: hydrate_opt_simple(self.protocol, file),
578            directory: hydrate_opt_simple(self.directory, file),
579            storage: hydrate_opt_simple(self.storage, file),
580            runner: hydrate_opt_simple(self.runner, file),
581            resolver: hydrate_opt_simple(self.resolver, file),
582            dictionary: hydrate_opt_simple(self.dictionary, file),
583            config: hydrate_opt_simple(self.config, file),
584            path: hydrate_opt_simple(self.path, file),
585            rights: hydrate_opt_simple(self.rights, file),
586            from: hydrate_opt_simple(self.from, file),
587            event_stream: hydrate_opt_simple(self.event_stream, file),
588            backing_dir: hydrate_opt_simple(self.backing_dir, file),
589            subdir: hydrate_opt_simple(self.subdir, file),
590            storage_id: hydrate_opt_simple(self.storage_id, file),
591            config_type: hydrate_opt_simple(self.config_type, file),
592            config_max_size: hydrate_opt_simple(self.config_max_size, file),
593            config_max_count: hydrate_opt_simple(self.config_max_count, file),
594            config_element_type: hydrate_opt_simple(self.config_element_type, file),
595            value: hydrate_opt_simple(self.value, file),
596            delivery: hydrate_opt_simple(self.delivery, file),
597        })
598    }
599}