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