Skip to main content

cml/types/
use.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::types::common::*;
6use crate::{
7    AnyRef, CanonicalizeContext, ConfigNestedValueType, ConfigType, DictionaryRef, Error,
8    EventScope, FromClauseContext,
9};
10
11use crate::one_or_many::{OneOrMany, always_one_context};
12use crate::types::right::{Rights, RightsClause};
13pub use cm_types::{
14    Availability, BorrowedName, DependencyType, HandleType, Name, OnTerminate, ParseError, Path,
15    RelativePath, StartupMode, 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/// A reference in a `use from`.
28#[derive(Debug, PartialEq, Eq, Hash, Clone, Reference)]
29#[reference(
30    expected = "\"parent\", \"framework\", \"debug\", \"self\", \"#<capability-name>\", \"#<child-name>\", \"#<collection-name>\", dictionary path, or none"
31)]
32pub enum UseFromRef {
33    /// A reference to the parent.
34    Parent,
35    /// A reference to the framework.
36    Framework,
37    /// A reference to debug.
38    Debug,
39    /// A reference to a child, collection, or a capability declared on self.
40    ///
41    /// A reference to a capability must be one of the following:
42    /// - A dictionary capability.
43    /// - A protocol that references a storage capability declared in the same component,
44    ///   which will cause the framework to host a fuchsia.sys2.StorageAdmin protocol for the
45    ///   component.
46    ///
47    /// A reference to a collection must be a service capability.
48    ///
49    /// This cannot be used to directly access capabilities that a component itself declares.
50    Named(Name),
51    /// A reference to this component.
52    Self_,
53    /// A reference to a dictionary.
54    Dictionary(DictionaryRef),
55}
56
57/// Example:
58///
59/// ```json5
60/// use: [
61///     {
62///         protocol: [
63///             "fuchsia.ui.scenic.Scenic",
64///             "fuchsia.accessibility.Manager",
65///         ]
66///     },
67///     {
68///         directory: "themes",
69///         path: "/data/themes",
70///         rights: [ "r*" ],
71///     },
72///     {
73///         storage: "persistent",
74///         path: "/data",
75///     },
76///     {
77///         event_stream: [
78///             "started",
79///             "stopped",
80///         ],
81///         from: "framework",
82///     },
83///     {
84///         runner: "own_test_runner".
85///         from: "#test_runner",
86///     },
87/// ],
88/// ```
89#[derive(Deserialize, Debug, Default, PartialEq, Clone, ReferenceDoc, Serialize)]
90#[serde(deny_unknown_fields)]
91#[reference_doc(fields_as = "list", top_level_doc_after_fields)]
92pub struct Use {
93    /// When using a service capability, the [name](#name) of a [service capability][doc-service].
94    #[serde(skip_serializing_if = "Option::is_none")]
95    #[reference_doc(skip = true)]
96    pub service: Option<OneOrMany<Name>>,
97
98    /// When using a protocol capability, the [name](#name) of a [protocol capability][doc-protocol].
99    #[serde(skip_serializing_if = "Option::is_none")]
100    #[reference_doc(skip = true)]
101    pub protocol: Option<OneOrMany<Name>>,
102
103    /// When using a directory capability, the [name](#name) of a [directory capability][doc-directory].
104    #[serde(skip_serializing_if = "Option::is_none")]
105    #[reference_doc(skip = true)]
106    pub directory: Option<Name>,
107
108    /// When using a storage capability, the [name](#name) of a [storage capability][doc-storage].
109    #[serde(skip_serializing_if = "Option::is_none")]
110    #[reference_doc(skip = true)]
111    pub storage: Option<Name>,
112
113    /// When using an event stream capability, the [name](#name) of an [event stream capability][doc-event].
114    #[serde(skip_serializing_if = "Option::is_none")]
115    #[reference_doc(skip = true)]
116    pub event_stream: Option<OneOrMany<Name>>,
117
118    /// When using a runner capability, the [name](#name) of a [runner capability][doc-runners].
119    #[serde(skip_serializing_if = "Option::is_none")]
120    #[reference_doc(skip = true)]
121    pub runner: Option<Name>,
122
123    /// When using a configuration capability, the [name](#name) of a [configuration capability][doc-configuration].
124    #[serde(skip_serializing_if = "Option::is_none")]
125    #[reference_doc(skip = true)]
126    pub config: Option<Name>,
127
128    /// When using a dictionary capability, the [name](#name) of a [dictionary capability][doc-dictionary].
129    #[serde(skip_serializing_if = "Option::is_none")]
130    #[reference_doc(skip = true)]
131    pub dictionary: Option<OneOrMany<Name>>,
132
133    /// The source of the capability. Defaults to `parent`.  One of:
134    /// - `parent`: The component's parent.
135    /// - `debug`: One of [`debug_capabilities`][fidl-environment-decl] in the
136    ///     environment assigned to this component.
137    /// - `framework`: The Component Framework runtime.
138    /// - `self`: This component.
139    /// - `#<capability-name>`: The name of another capability from which the
140    ///     requested capability is derived.
141    /// - `#<child-name>`: A [reference](#references) to a child component
142    ///     instance.
143    ///
144    /// [fidl-environment-decl]: /reference/fidl/fuchsia.component.decl#Environment
145    #[serde(skip_serializing_if = "Option::is_none")]
146    pub from: Option<UseFromRef>,
147
148    /// The path at which to install the capability in the component's namespace. For protocols,
149    /// defaults to `/svc/${protocol}`.  Required for `directory` and `storage`. This property is
150    /// disallowed for declarations with arrays of capability names and for runner capabilities.
151    #[serde(skip_serializing_if = "Option::is_none")]
152    pub path: Option<Path>,
153
154    /// A processargs ordinal (aka. "numbered handle") over which a channel to this protocol will
155    /// be delivered to the component's processargs.
156    ///
157    // TODO: We could support strings like "PA_*", but it's not clear that's necessary since usage
158    // of this feature is expected to be limited.
159    #[serde(skip_serializing_if = "Option::is_none")]
160    pub numbered_handle: Option<HandleType>,
161
162    /// (`directory` only) the maximum [directory rights][doc-directory-rights] to apply to
163    /// the directory in the component's namespace.
164    #[serde(skip_serializing_if = "Option::is_none")]
165    #[reference_doc(json_type = "array of string")]
166    pub rights: Option<Rights>,
167
168    /// (`directory` only) A subdirectory within the directory capability to provide in the
169    /// component's namespace.
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub subdir: Option<RelativePath>,
172
173    /// (`event_stream` only) When defined the event stream will contain events about only the
174    /// components defined in the scope.
175    #[serde(skip_serializing_if = "Option::is_none")]
176    pub scope: Option<OneOrMany<EventScope>>,
177
178    /// (`event_stream` only) Capability requested event streams require specifying a filter
179    /// referring to the protocol to which the events in the event stream apply. The content of the
180    /// filter will be an object mapping from "name" to the "protocol name".
181    #[serde(skip_serializing_if = "Option::is_none")]
182    pub filter: Option<Map<String, Value>>,
183
184    /// The type of dependency between the source and
185    /// this component, one of:
186    /// - `strong`: a strong dependency, which is used to determine shutdown
187    ///     ordering. Component manager is guaranteed to stop the target before the
188    ///     source. This is the default.
189    /// - `weak`: a weak dependency, which is ignored during shutdown. When component manager
190    ///     stops the parent realm, the source may stop before the clients. Clients of weak
191    ///     dependencies must be able to handle these dependencies becoming unavailable.
192    /// This property is disallowed for runner capabilities, which are always a `strong` dependency.
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub dependency: Option<DependencyType>,
195
196    /// The expectations around this capability's availability. One
197    /// of:
198    /// - `required` (default): a required dependency, the component is unable to perform its
199    ///     work without this capability.
200    /// - `optional`: an optional dependency, the component will be able to function without this
201    ///     capability (although if the capability is unavailable some functionality may be
202    ///     disabled).
203    /// - `transitional`: the source may omit the route completely without even having to route
204    ///     from `void`. Used for soft transitions that introduce new capabilities.
205    /// This property is disallowed for runner capabilities, which are always `required`.
206    ///
207    /// For more information, see the
208    /// [availability](/docs/concepts/components/v2/capabilities/availability.md) documentation.
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub availability: Option<Availability>,
211
212    /// (`config` only) The configuration key in the component's `config` block that this capability
213    /// will set.
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub key: Option<Name>,
216
217    /// (`config` only) The type of configuration, one of:
218    /// - `bool`: Boolean type.
219    /// - `uint8`: Unsigned 8 bit type.
220    /// - `uint16`: Unsigned 16 bit type.
221    /// - `uint32`: Unsigned 32 bit type.
222    /// - `uint64`: Unsigned 64 bit type.
223    /// - `int8`: Signed 8 bit type.
224    /// - `int16`: Signed 16 bit type.
225    /// - `int32`: Signed 32 bit type.
226    /// - `int64`: Signed 64 bit type.
227    /// - `string`: ASCII string type.
228    /// - `vector`: Vector type. See `element` for the type of the element within the vector
229    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
230    #[reference_doc(rename = "type")]
231    pub config_type: Option<ConfigType>,
232
233    /// (`configuration` only) Only supported if this configuration `type` is 'string'.
234    /// This is the max size of the string.
235    #[serde(rename = "max_size", skip_serializing_if = "Option::is_none")]
236    #[reference_doc(rename = "max_size")]
237    pub config_max_size: Option<NonZeroU32>,
238
239    /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
240    /// This is the max number of elements in the vector.
241    #[serde(rename = "max_count", skip_serializing_if = "Option::is_none")]
242    #[reference_doc(rename = "max_count")]
243    pub config_max_count: Option<NonZeroU32>,
244
245    /// (`configuration` only) Only supported if this configuration `type` is 'vector'.
246    /// This is the type of the elements in the configuration vector.
247    ///
248    /// Example (simple type):
249    ///
250    /// ```json5
251    /// { type: "uint8" }
252    /// ```
253    ///
254    /// Example (string type):
255    ///
256    /// ```json5
257    /// {
258    ///   type: "string",
259    ///   max_size: 100,
260    /// }
261    /// ```
262    #[serde(rename = "element", skip_serializing_if = "Option::is_none")]
263    #[reference_doc(rename = "element", json_type = "object")]
264    pub config_element_type: Option<ConfigNestedValueType>,
265
266    /// (`configuration` only) The default value of this configuration.
267    /// Default values are used if the capability is optional and routed from `void`.
268    /// This is only supported if `availability` is not `required``.
269    #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
270    #[reference_doc(rename = "default")]
271    pub config_default: Option<serde_json::Value>,
272}
273
274#[derive(Debug, Clone, Default, Serialize)]
275pub struct ContextUse {
276    #[serde(skip)]
277    pub origin: Arc<PathBuf>,
278
279    #[serde(skip_serializing_if = "Option::is_none")]
280    pub service: Option<ContextSpanned<OneOrMany<Name>>>,
281
282    #[serde(skip_serializing_if = "Option::is_none")]
283    pub protocol: Option<ContextSpanned<OneOrMany<Name>>>,
284
285    #[serde(skip_serializing_if = "Option::is_none")]
286    pub directory: Option<ContextSpanned<Name>>,
287
288    #[serde(skip_serializing_if = "Option::is_none")]
289    pub storage: Option<ContextSpanned<Name>>,
290
291    #[serde(skip_serializing_if = "Option::is_none")]
292    pub event_stream: Option<ContextSpanned<OneOrMany<Name>>>,
293
294    #[serde(skip_serializing_if = "Option::is_none")]
295    pub runner: Option<ContextSpanned<Name>>,
296
297    #[serde(skip_serializing_if = "Option::is_none")]
298    pub config: Option<ContextSpanned<Name>>,
299
300    #[serde(skip_serializing_if = "Option::is_none")]
301    pub dictionary: Option<ContextSpanned<OneOrMany<Name>>>,
302
303    #[serde(skip_serializing_if = "Option::is_none")]
304    pub from: Option<ContextSpanned<UseFromRef>>,
305
306    #[serde(skip_serializing_if = "Option::is_none")]
307    pub path: Option<ContextSpanned<Path>>,
308
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub numbered_handle: Option<ContextSpanned<HandleType>>,
311
312    #[serde(skip_serializing_if = "Option::is_none")]
313    pub rights: Option<ContextSpanned<Rights>>,
314
315    #[serde(skip_serializing_if = "Option::is_none")]
316    pub subdir: Option<ContextSpanned<RelativePath>>,
317
318    #[serde(skip_serializing_if = "Option::is_none")]
319    pub scope: Option<ContextSpanned<OneOrMany<EventScope>>>,
320
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub filter: Option<ContextSpanned<Map<String, Value>>>,
323
324    #[serde(skip_serializing_if = "Option::is_none")]
325    pub dependency: Option<ContextSpanned<DependencyType>>,
326
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub availability: Option<ContextSpanned<Availability>>,
329
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub key: Option<ContextSpanned<Name>>,
332
333    #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
334    pub config_type: Option<ContextSpanned<ConfigType>>,
335
336    #[serde(rename = "max_size", skip_serializing_if = "Option::is_none")]
337    pub config_max_size: Option<ContextSpanned<NonZeroU32>>,
338
339    #[serde(rename = "max_count", skip_serializing_if = "Option::is_none")]
340    pub config_max_count: Option<ContextSpanned<NonZeroU32>>,
341
342    #[serde(rename = "element", skip_serializing_if = "Option::is_none")]
343    pub config_element_type: Option<ContextSpanned<ConfigNestedValueType>>,
344
345    #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
346    pub config_default: Option<ContextSpanned<serde_json::Value>>,
347}
348
349impl ContextCapabilityClause for ContextUse {
350    fn service(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
351        option_one_or_many_as_ref_context(&self.service)
352    }
353    fn protocol(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
354        option_one_or_many_as_ref_context(&self.protocol)
355    }
356    fn directory(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
357        self.directory.as_ref().map(|s| ContextSpanned {
358            value: OneOrMany::One((s.value).as_ref()),
359            origin: s.origin.clone(),
360        })
361    }
362    fn storage(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
363        self.storage.as_ref().map(|s| ContextSpanned {
364            value: OneOrMany::One((s.value).as_ref()),
365            origin: s.origin.clone(),
366        })
367    }
368    fn runner(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
369        self.runner.as_ref().map(|s| ContextSpanned {
370            value: OneOrMany::One((s.value).as_ref()),
371            origin: s.origin.clone(),
372        })
373    }
374    fn resolver(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
375        None
376    }
377    fn event_stream(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
378        option_one_or_many_as_ref_context(&self.event_stream)
379    }
380    fn dictionary(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
381        option_one_or_many_as_ref_context(&self.dictionary)
382    }
383    fn config(&self) -> Option<ContextSpanned<OneOrMany<&BorrowedName>>> {
384        self.config.as_ref().map(|s| ContextSpanned {
385            value: OneOrMany::One((s.value).as_ref()),
386            origin: s.origin.clone(),
387        })
388    }
389
390    fn decl_type(&self) -> &'static str {
391        "use"
392    }
393    fn supported(&self) -> &[&'static str] {
394        &[
395            "service",
396            "protocol",
397            "directory",
398            "storage",
399            "event_stream",
400            "runner",
401            "config",
402            "dictionary",
403        ]
404    }
405
406    fn are_many_names_allowed(&self) -> bool {
407        ["service", "protocol", "event_stream"].contains(&self.capability_type(None).unwrap())
408    }
409
410    fn set_service(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
411        self.service = o;
412    }
413    fn set_protocol(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
414        self.protocol = o;
415    }
416    fn set_directory(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
417        self.directory = always_one_context(o);
418    }
419    fn set_storage(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
420        self.storage = always_one_context(o);
421    }
422    fn set_runner(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
423    fn set_resolver(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
424    fn set_event_stream(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
425        self.event_stream = o;
426    }
427    fn set_dictionary(&mut self, _o: Option<ContextSpanned<OneOrMany<Name>>>) {}
428    fn set_config(&mut self, o: Option<ContextSpanned<OneOrMany<Name>>>) {
429        self.config = always_one_context(o);
430    }
431
432    fn origin(&self) -> &Arc<PathBuf> {
433        &self.origin
434    }
435
436    fn availability(&self) -> Option<ContextSpanned<Availability>> {
437        self.availability.clone()
438    }
439    fn set_availability(&mut self, a: Option<ContextSpanned<Availability>>) {
440        self.availability = a;
441    }
442}
443
444impl CanonicalizeContext for ContextUse {
445    fn canonicalize_context(&mut self) {
446        // Sort the names of the capabilities. Only capabilities with OneOrMany values are included here.
447        if let Some(service) = &mut self.service {
448            service.value.canonicalize_context();
449        } else if let Some(protocol) = &mut self.protocol {
450            protocol.value.canonicalize_context();
451        } else if let Some(event_stream) = &mut self.event_stream {
452            event_stream.value.canonicalize_context();
453            if let Some(scope) = &mut self.scope {
454                scope.value.canonicalize_context();
455            }
456        }
457    }
458}
459
460impl FromClauseContext for ContextUse {
461    fn from_(&self) -> ContextSpanned<OneOrMany<AnyRef<'_>>> {
462        match &self.from {
463            Some(from) => {
464                return ContextSpanned {
465                    value: OneOrMany::One(AnyRef::from(&from.value)),
466                    origin: from.origin.clone(),
467                };
468            }
469            // Default for `use`.
470            None => {
471                return ContextSpanned {
472                    value: OneOrMany::One(AnyRef::Parent),
473                    origin: self.origin.clone(),
474                };
475            }
476        }
477    }
478}
479
480impl RightsClause for ContextUse {
481    fn rights(&self) -> Option<&Rights> {
482        self.rights.as_ref().map(|r| &r.value)
483    }
484}
485
486impl PartialEq for ContextUse {
487    fn eq(&self, other: &Self) -> bool {
488        macro_rules! cmp {
489            ($field:ident) => {
490                match (&self.$field, &other.$field) {
491                    (Some(a), Some(b)) => a.value == b.value,
492                    (None, None) => true,
493                    _ => false,
494                }
495            };
496        }
497
498        cmp!(service)
499            && cmp!(protocol)
500            && cmp!(directory)
501            && cmp!(storage)
502            && cmp!(event_stream)
503            && cmp!(runner)
504            && cmp!(config)
505            && cmp!(dictionary)
506            && cmp!(from)
507            && cmp!(path)
508            && cmp!(numbered_handle)
509            && cmp!(rights)
510            && cmp!(subdir)
511            && cmp!(scope)
512            && cmp!(filter)
513            && cmp!(dependency)
514            && cmp!(availability)
515            && cmp!(key)
516            && cmp!(config_type)
517            && cmp!(config_max_size)
518            && cmp!(config_max_count)
519            && cmp!(config_element_type)
520            && cmp!(config_default)
521    }
522}
523
524impl Eq for ContextUse {}
525
526impl ContextPathClause for ContextUse {
527    fn path(&self) -> Option<&ContextSpanned<Path>> {
528        self.path.as_ref()
529    }
530}
531
532impl Hydrate for Use {
533    type Output = ContextUse;
534
535    fn hydrate(self, file: &Arc<PathBuf>) -> Result<Self::Output, Error> {
536        Ok(ContextUse {
537            origin: file.clone(),
538            service: hydrate_opt_simple(self.service, file),
539            protocol: hydrate_opt_simple(self.protocol, file),
540            directory: hydrate_opt_simple(self.directory, file),
541            storage: hydrate_opt_simple(self.storage, file),
542            event_stream: hydrate_opt_simple(self.event_stream, file),
543            runner: hydrate_opt_simple(self.runner, file),
544            config: hydrate_opt_simple(self.config, file),
545            dictionary: hydrate_opt_simple(self.dictionary, file),
546            from: hydrate_opt_simple(self.from, file),
547            path: hydrate_opt_simple(self.path, file),
548            numbered_handle: hydrate_opt_simple(self.numbered_handle, file),
549            rights: hydrate_opt_simple(self.rights, file),
550            subdir: hydrate_opt_simple(self.subdir, file),
551            scope: hydrate_opt_simple(self.scope, file),
552            filter: hydrate_opt_simple(self.filter, file),
553            dependency: hydrate_opt_simple(self.dependency, file),
554            availability: hydrate_opt_simple(self.availability, file),
555            key: hydrate_opt_simple(self.key, file),
556            config_type: hydrate_opt_simple(self.config_type, file),
557            config_max_size: hydrate_opt_simple(self.config_max_size, file),
558            config_max_count: hydrate_opt_simple(self.config_max_count, file),
559            config_element_type: hydrate_opt_simple(self.config_element_type, file),
560            config_default: hydrate_opt_simple(self.config_default, file),
561        })
562    }
563}