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