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