Skip to main content

cm_rust/
lib.rs

1// Copyright 2019 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 cm_rust_derive::{ExposeDeclCommon, ExposeDeclCommonAlwaysRequired, FidlDecl, ToGenericRef};
6use cm_types::{AllowedOffers, BorrowedSeparatedPath, LongName, Name, Path, RelativePath, Url};
7use directed_graph::DirectedGraph;
8use fidl_fuchsia_component_decl_common as fdecl;
9use fidl_fuchsia_data_common as fdata;
10use fidl_fuchsia_io_common as fio;
11use fidl_fuchsia_sys2_common as fsys;
12use from_enum::FromEnum;
13use std::collections::{BTreeMap, HashMap};
14use std::hash::Hash;
15use std::sync::LazyLock;
16use std::{fmt, mem};
17use strum_macros::EnumIter;
18use thiserror::Error;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23#[cfg(feature = "serde")]
24mod serde_ext;
25
26pub mod capability;
27pub mod config;
28pub mod offer;
29pub mod r#use;
30
31pub use crate::capability::*;
32pub use crate::config::*;
33#[allow(unused_imports)]
34pub use crate::offer::*; // TODO: remove glob after refactor lands
35pub use crate::r#use::*;
36
37/// Converts a fidl object into its corresponding native representation.
38pub trait FidlIntoNative<T> {
39    fn fidl_into_native(self) -> T;
40}
41
42impl<Native, Fidl> FidlIntoNative<Box<[Native]>> for Vec<Fidl>
43where
44    Fidl: FidlIntoNative<Native>,
45{
46    fn fidl_into_native(self) -> Box<[Native]> {
47        IntoIterator::into_iter(self).map(|s| s.fidl_into_native()).collect()
48    }
49}
50
51pub trait NativeIntoFidl<T> {
52    fn native_into_fidl(self) -> T;
53}
54
55impl<Native, Fidl> NativeIntoFidl<Vec<Fidl>> for Box<[Native]>
56where
57    Native: NativeIntoFidl<Fidl>,
58{
59    fn native_into_fidl(self) -> Vec<Fidl> {
60        IntoIterator::into_iter(self).map(|s| s.native_into_fidl()).collect()
61    }
62}
63
64impl FidlIntoNative<Name> for String {
65    fn fidl_into_native(self) -> Name {
66        // cm_fidl_validator should have already validated this
67        self.parse().unwrap()
68    }
69}
70
71impl NativeIntoFidl<String> for Name {
72    fn native_into_fidl(self) -> String {
73        self.to_string()
74    }
75}
76
77impl FidlIntoNative<LongName> for String {
78    fn fidl_into_native(self) -> LongName {
79        // cm_fidl_validator should have already validated this
80        self.parse().unwrap()
81    }
82}
83
84impl NativeIntoFidl<String> for LongName {
85    fn native_into_fidl(self) -> String {
86        self.to_string()
87    }
88}
89
90impl FidlIntoNative<Path> for String {
91    fn fidl_into_native(self) -> Path {
92        // cm_fidl_validator should have already validated this
93        self.parse().unwrap()
94    }
95}
96
97impl NativeIntoFidl<String> for Path {
98    fn native_into_fidl(self) -> String {
99        self.to_string()
100    }
101}
102
103impl FidlIntoNative<RelativePath> for String {
104    fn fidl_into_native(self) -> RelativePath {
105        // cm_fidl_validator should have already validated this
106        self.parse().unwrap()
107    }
108}
109
110impl NativeIntoFidl<String> for RelativePath {
111    fn native_into_fidl(self) -> String {
112        self.to_string()
113    }
114}
115
116impl NativeIntoFidl<Option<String>> for RelativePath {
117    fn native_into_fidl(self) -> Option<String> {
118        if self.is_dot() { None } else { Some(self.to_string()) }
119    }
120}
121
122impl FidlIntoNative<Url> for String {
123    fn fidl_into_native(self) -> Url {
124        // cm_fidl_validator should have already validated this
125        self.parse().unwrap()
126    }
127}
128
129impl NativeIntoFidl<String> for Url {
130    fn native_into_fidl(self) -> String {
131        self.to_string()
132    }
133}
134
135impl<F, N> FidlIntoNative<Box<N>> for F
136where
137    F: FidlIntoNative<N>,
138{
139    fn fidl_into_native(self) -> Box<N> {
140        Box::new(self.fidl_into_native())
141    }
142}
143
144impl<N, F> NativeIntoFidl<F> for Box<N>
145where
146    N: NativeIntoFidl<F>,
147{
148    fn native_into_fidl(self) -> F {
149        (*self).native_into_fidl()
150    }
151}
152
153/// Generates `FidlIntoNative` and `NativeIntoFidl` implementations that leaves the input unchanged.
154macro_rules! fidl_translations_identical {
155    ($into_type:ty) => {
156        impl FidlIntoNative<$into_type> for $into_type {
157            fn fidl_into_native(self) -> $into_type {
158                self
159            }
160        }
161        impl NativeIntoFidl<$into_type> for $into_type {
162            fn native_into_fidl(self) -> Self {
163                self
164            }
165        }
166    };
167}
168
169/// Generates `FidlIntoNative` and `NativeIntoFidl` implementations that
170/// delegate to existing `Into` implementations.
171macro_rules! fidl_translations_from_into {
172    ($native_type:ty, $fidl_type:ty) => {
173        impl FidlIntoNative<$native_type> for $fidl_type {
174            fn fidl_into_native(self) -> $native_type {
175                self.into()
176            }
177        }
178        impl NativeIntoFidl<$fidl_type> for $native_type {
179            fn native_into_fidl(self) -> $fidl_type {
180                self.into()
181            }
182        }
183    };
184}
185
186/// Generates `FidlIntoNative` and `NativeIntoFidl` implementations for
187/// an symmetrical enum types.
188/// `fidl_type` should be the FIDL type while `native_type` should be
189/// the Rust native type defined elsewhere in this file.
190/// Each field of the enums must be provided in the `variant` fieldset.
191macro_rules! fidl_translations_symmetrical_enums {
192($fidl_type:ty , $native_type:ty, $($variant: ident),*) => {
193        impl FidlIntoNative<$native_type> for $fidl_type {
194            fn fidl_into_native(self) -> $native_type {
195                match self {
196                    $( <$fidl_type>::$variant => <$native_type>::$variant,  )*
197                }
198            }
199        }
200        impl NativeIntoFidl<$fidl_type> for $native_type {
201            fn native_into_fidl(self) -> $fidl_type {
202                match self {
203                    $( <$native_type>::$variant => <$fidl_type>::$variant,  )*
204                }
205            }
206        }
207    };
208}
209
210#[derive(FidlDecl, Debug, Clone, PartialEq, Default)]
211#[fidl_decl(fidl_table = "fdecl::Component")]
212pub struct ComponentDecl {
213    pub program: Option<ProgramDecl>,
214    pub uses: Box<[UseDecl]>,
215    pub exposes: Box<[ExposeDecl]>,
216    pub offers: Box<[OfferDecl]>,
217    pub capabilities: Box<[CapabilityDecl]>,
218    pub children: Box<[ChildDecl]>,
219    pub collections: Box<[CollectionDecl]>,
220    pub facets: Option<fdata::Dictionary>,
221    pub environments: Box<[EnvironmentDecl]>,
222    pub config: Option<ConfigDecl>,
223    #[cfg(fuchsia_api_level_at_least = "31")]
224    pub debug_info: Option<DebugInfo>,
225}
226
227impl ComponentDecl {
228    /// Returns the runner used by this component, or `None` if this is a non-executable component.
229    #[cfg(fuchsia_api_level_at_least = "HEAD")]
230    pub fn get_runner(&self) -> Option<UseRunnerDecl> {
231        self.program
232            .as_ref()
233            .and_then(|p| p.runner.as_ref())
234            .map(|r| UseRunnerDecl {
235                source: UseSource::Environment,
236                source_name: r.clone(),
237                source_dictionary: Default::default(),
238            })
239            .or_else(|| {
240                self.uses.iter().find_map(|u| match u {
241                    UseDecl::Runner(r) => Some(r.clone()),
242                    _ => None,
243                })
244            })
245    }
246
247    /// Returns the `StorageDecl` corresponding to `storage_name`.
248    pub fn find_storage_source<'a>(&'a self, storage_name: &Name) -> Option<&'a StorageDecl> {
249        self.capabilities.iter().find_map(|c| match c {
250            CapabilityDecl::Storage(s) if &s.name == storage_name => Some(s),
251            _ => None,
252        })
253    }
254
255    /// Returns the `ProtocolDecl` corresponding to `protocol_name`.
256    pub fn find_protocol_source<'a>(&'a self, protocol_name: &Name) -> Option<&'a ProtocolDecl> {
257        self.capabilities.iter().find_map(|c| match c {
258            CapabilityDecl::Protocol(r) if &r.name == protocol_name => Some(r),
259            _ => None,
260        })
261    }
262
263    /// Returns the `DirectoryDecl` corresponding to `directory_name`.
264    pub fn find_directory_source<'a>(&'a self, directory_name: &Name) -> Option<&'a DirectoryDecl> {
265        self.capabilities.iter().find_map(|c| match c {
266            CapabilityDecl::Directory(r) if &r.name == directory_name => Some(r),
267            _ => None,
268        })
269    }
270
271    /// Returns the `RunnerDecl` corresponding to `runner_name`.
272    pub fn find_runner_source<'a>(&'a self, runner_name: &Name) -> Option<&'a RunnerDecl> {
273        self.capabilities.iter().find_map(|c| match c {
274            CapabilityDecl::Runner(r) if &r.name == runner_name => Some(r),
275            _ => None,
276        })
277    }
278
279    /// Returns the `ResolverDecl` corresponding to `resolver_name`.
280    pub fn find_resolver_source<'a>(&'a self, resolver_name: &Name) -> Option<&'a ResolverDecl> {
281        self.capabilities.iter().find_map(|c| match c {
282            CapabilityDecl::Resolver(r) if &r.name == resolver_name => Some(r),
283            _ => None,
284        })
285    }
286
287    /// Returns the `CollectionDecl` corresponding to `collection_name`.
288    pub fn find_collection<'a>(&'a self, collection_name: &str) -> Option<&'a CollectionDecl> {
289        self.collections.iter().find(|c| c.name == collection_name)
290    }
291
292    /// Indicates whether the capability specified by `target_name` is exposed to the framework.
293    pub fn is_protocol_exposed_to_framework(&self, in_target_name: &Name) -> bool {
294        self.exposes.iter().any(|expose| match expose {
295            ExposeDecl::Protocol(ExposeProtocolDecl { target, target_name, .. })
296                if target == &ExposeTarget::Framework =>
297            {
298                target_name == in_target_name
299            }
300            _ => false,
301        })
302    }
303
304    /// Indicates whether the capability specified by `source_name` is requested.
305    pub fn uses_protocol(&self, source_name: &Name) -> bool {
306        self.uses.iter().any(|use_decl| match use_decl {
307            UseDecl::Protocol(ls) => &ls.source_name == source_name,
308            _ => false,
309        })
310    }
311}
312
313pub use cm_types::Availability;
314
315fidl_translations_symmetrical_enums!(
316    fdecl::Availability,
317    Availability,
318    Required,
319    Optional,
320    SameAsTarget,
321    Transitional
322);
323
324pub use cm_types::DeliveryType;
325
326#[cfg(fuchsia_api_level_at_least = "HEAD")]
327impl FidlIntoNative<DeliveryType> for fdecl::DeliveryType {
328    fn fidl_into_native(self) -> DeliveryType {
329        self.try_into().unwrap()
330    }
331}
332
333#[cfg(fuchsia_api_level_at_least = "HEAD")]
334impl NativeIntoFidl<fdecl::DeliveryType> for DeliveryType {
335    fn native_into_fidl(self) -> fdecl::DeliveryType {
336        self.into()
337    }
338}
339
340pub trait SourcePath {
341    fn source_path(&self) -> BorrowedSeparatedPath<'_>;
342    fn is_from_dictionary(&self) -> bool {
343        !self.source_path().dirname.is_dot()
344    }
345}
346
347#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
348#[derive(Debug, Clone, PartialEq, Eq)]
349pub struct NameMapping {
350    pub source_name: Name,
351    pub target_name: Name,
352}
353
354impl NativeIntoFidl<fdecl::NameMapping> for NameMapping {
355    fn native_into_fidl(self) -> fdecl::NameMapping {
356        fdecl::NameMapping {
357            source_name: self.source_name.native_into_fidl(),
358            target_name: self.target_name.native_into_fidl(),
359        }
360    }
361}
362
363impl FidlIntoNative<NameMapping> for fdecl::NameMapping {
364    fn fidl_into_native(self) -> NameMapping {
365        NameMapping {
366            source_name: self.source_name.fidl_into_native(),
367            target_name: self.target_name.fidl_into_native(),
368        }
369    }
370}
371
372#[cfg_attr(
373    feature = "serde",
374    derive(Deserialize, Serialize),
375    serde(tag = "type", rename_all = "snake_case")
376)]
377#[derive(FidlDecl, FromEnum, Debug, Clone, PartialEq, Eq)]
378#[fidl_decl(fidl_union = "fdecl::Expose")]
379pub enum ExposeDecl {
380    Service(ExposeServiceDecl),
381    Protocol(ExposeProtocolDecl),
382    Directory(ExposeDirectoryDecl),
383    Runner(ExposeRunnerDecl),
384    Resolver(ExposeResolverDecl),
385    Dictionary(ExposeDictionaryDecl),
386    Config(ExposeConfigurationDecl),
387}
388
389impl SourceName for ExposeDecl {
390    fn source_name(&self) -> &Name {
391        match self {
392            Self::Service(e) => e.source_name(),
393            Self::Protocol(e) => e.source_name(),
394            Self::Directory(e) => e.source_name(),
395            Self::Runner(e) => e.source_name(),
396            Self::Resolver(e) => e.source_name(),
397            Self::Dictionary(e) => e.source_name(),
398            Self::Config(e) => e.source_name(),
399        }
400    }
401}
402
403impl SourcePath for ExposeDecl {
404    fn source_path(&self) -> BorrowedSeparatedPath<'_> {
405        match self {
406            Self::Service(e) => e.source_path(),
407            Self::Protocol(e) => e.source_path(),
408            Self::Directory(e) => e.source_path(),
409            Self::Runner(e) => e.source_path(),
410            Self::Resolver(e) => e.source_path(),
411            Self::Dictionary(e) => e.source_path(),
412            Self::Config(e) => e.source_path(),
413        }
414    }
415}
416
417impl ExposeDeclCommon for ExposeDecl {
418    fn source(&self) -> &ExposeSource {
419        match self {
420            Self::Service(e) => e.source(),
421            Self::Protocol(e) => e.source(),
422            Self::Directory(e) => e.source(),
423            Self::Runner(e) => e.source(),
424            Self::Resolver(e) => e.source(),
425            Self::Dictionary(e) => e.source(),
426            Self::Config(e) => e.source(),
427        }
428    }
429
430    fn target(&self) -> &ExposeTarget {
431        match self {
432            Self::Service(e) => e.target(),
433            Self::Protocol(e) => e.target(),
434            Self::Directory(e) => e.target(),
435            Self::Runner(e) => e.target(),
436            Self::Resolver(e) => e.target(),
437            Self::Dictionary(e) => e.target(),
438            Self::Config(e) => e.target(),
439        }
440    }
441
442    fn target_name(&self) -> &Name {
443        match self {
444            Self::Service(e) => e.target_name(),
445            Self::Protocol(e) => e.target_name(),
446            Self::Directory(e) => e.target_name(),
447            Self::Runner(e) => e.target_name(),
448            Self::Resolver(e) => e.target_name(),
449            Self::Dictionary(e) => e.target_name(),
450            Self::Config(e) => e.target_name(),
451        }
452    }
453
454    fn availability(&self) -> &Availability {
455        match self {
456            Self::Service(e) => e.availability(),
457            Self::Protocol(e) => e.availability(),
458            Self::Directory(e) => e.availability(),
459            Self::Runner(e) => e.availability(),
460            Self::Resolver(e) => e.availability(),
461            Self::Dictionary(e) => e.availability(),
462            Self::Config(e) => e.availability(),
463        }
464    }
465}
466
467#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
468#[derive(FidlDecl, ExposeDeclCommon, Debug, Clone, PartialEq, Eq)]
469#[fidl_decl(fidl_table = "fdecl::ExposeService", source_path = "dictionary")]
470pub struct ExposeServiceDecl {
471    pub source: ExposeSource,
472    pub source_name: Name,
473    #[fidl_decl(default_preserve_none)]
474    pub source_dictionary: RelativePath,
475    pub target: ExposeTarget,
476    pub target_name: Name,
477    #[fidl_decl(default)]
478    pub availability: Availability,
479}
480
481#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
482#[derive(FidlDecl, ExposeDeclCommon, Debug, Clone, PartialEq, Eq)]
483#[fidl_decl(fidl_table = "fdecl::ExposeProtocol", source_path = "dictionary")]
484pub struct ExposeProtocolDecl {
485    pub source: ExposeSource,
486    pub source_name: Name,
487    #[fidl_decl(default_preserve_none)]
488    pub source_dictionary: RelativePath,
489    pub target: ExposeTarget,
490    pub target_name: Name,
491    #[fidl_decl(default)]
492    pub availability: Availability,
493}
494
495#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
496#[derive(FidlDecl, ExposeDeclCommon, Debug, Clone, PartialEq, Eq)]
497#[fidl_decl(fidl_table = "fdecl::ExposeDirectory", source_path = "dictionary")]
498pub struct ExposeDirectoryDecl {
499    pub source: ExposeSource,
500    pub source_name: Name,
501    #[fidl_decl(default_preserve_none)]
502    pub source_dictionary: RelativePath,
503    pub target: ExposeTarget,
504    pub target_name: Name,
505
506    #[cfg_attr(
507        feature = "serde",
508        serde(
509            deserialize_with = "serde_ext::deserialize_opt_fio_operations",
510            serialize_with = "serde_ext::serialize_opt_fio_operations"
511        )
512    )]
513    pub rights: Option<fio::Operations>,
514
515    #[fidl_decl(default_preserve_none)]
516    pub subdir: RelativePath,
517
518    #[fidl_decl(default)]
519    pub availability: Availability,
520}
521
522#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
523#[derive(FidlDecl, ExposeDeclCommonAlwaysRequired, Debug, Clone, PartialEq, Eq)]
524#[fidl_decl(fidl_table = "fdecl::ExposeRunner", source_path = "dictionary")]
525pub struct ExposeRunnerDecl {
526    pub source: ExposeSource,
527    pub source_name: Name,
528    #[fidl_decl(default_preserve_none)]
529    pub source_dictionary: RelativePath,
530    pub target: ExposeTarget,
531    pub target_name: Name,
532}
533
534#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
535#[derive(FidlDecl, ExposeDeclCommonAlwaysRequired, Debug, Clone, PartialEq, Eq)]
536#[fidl_decl(fidl_table = "fdecl::ExposeResolver", source_path = "dictionary")]
537pub struct ExposeResolverDecl {
538    pub source: ExposeSource,
539    pub source_name: Name,
540    #[fidl_decl(default_preserve_none)]
541    pub source_dictionary: RelativePath,
542    pub target: ExposeTarget,
543    pub target_name: Name,
544}
545
546#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
547#[derive(FidlDecl, ExposeDeclCommon, Debug, Clone, PartialEq, Eq)]
548#[fidl_decl(fidl_table = "fdecl::ExposeDictionary", source_path = "dictionary")]
549pub struct ExposeDictionaryDecl {
550    pub source: ExposeSource,
551    pub source_name: Name,
552    #[fidl_decl(default_preserve_none)]
553    pub source_dictionary: RelativePath,
554    pub target: ExposeTarget,
555    pub target_name: Name,
556    #[fidl_decl(default)]
557    pub availability: Availability,
558}
559
560#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
561#[derive(FidlDecl, ExposeDeclCommon, Debug, Clone, PartialEq, Eq)]
562#[fidl_decl(fidl_table = "fdecl::ExposeConfiguration", source_path = "name_only")]
563pub struct ExposeConfigurationDecl {
564    pub source: ExposeSource,
565    pub source_name: Name,
566    pub target: ExposeTarget,
567    pub target_name: Name,
568    #[fidl_decl(default_preserve_none)]
569    pub source_dictionary: RelativePath,
570    #[fidl_decl(default)]
571    pub availability: Availability,
572}
573
574#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
575#[fidl_decl(fidl_table = "fdecl::Child")]
576pub struct ChildDecl {
577    pub name: LongName,
578    pub url: Url,
579    pub startup: fdecl::StartupMode,
580    pub on_terminate: Option<fdecl::OnTerminate>,
581    pub environment: Option<Name>,
582    pub config_overrides: Option<Box<[ConfigOverride]>>,
583}
584
585#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
586#[derive(Debug, Clone, PartialEq, Eq, Hash)]
587pub struct ChildRef {
588    pub name: LongName,
589    pub collection: Option<Name>,
590}
591
592impl std::fmt::Display for ChildRef {
593    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
594        if let Some(collection) = &self.collection {
595            write!(f, "{}:{}", collection, self.name)
596        } else {
597            write!(f, "{}", self.name)
598        }
599    }
600}
601
602impl FidlIntoNative<ChildRef> for fdecl::ChildRef {
603    fn fidl_into_native(self) -> ChildRef {
604        // cm_fidl_validator should have already validated this
605        ChildRef {
606            name: self.name.parse().unwrap(),
607            collection: self.collection.map(|c| c.parse().unwrap()),
608        }
609    }
610}
611
612impl NativeIntoFidl<fdecl::ChildRef> for ChildRef {
613    fn native_into_fidl(self) -> fdecl::ChildRef {
614        fdecl::ChildRef {
615            name: self.name.to_string(),
616            collection: self.collection.map(|c| c.to_string()),
617        }
618    }
619}
620
621#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
622#[fidl_decl(fidl_table = "fdecl::Collection")]
623pub struct CollectionDecl {
624    pub name: Name,
625    pub durability: fdecl::Durability,
626    pub environment: Option<Name>,
627
628    #[fidl_decl(default)]
629    pub allowed_offers: AllowedOffers,
630    #[fidl_decl(default)]
631    pub allow_long_names: bool,
632
633    pub persistent_storage: Option<bool>,
634}
635
636#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
637#[fidl_decl(fidl_table = "fdecl::Environment")]
638pub struct EnvironmentDecl {
639    pub name: Name,
640    pub extends: fdecl::EnvironmentExtends,
641    pub runners: Box<[RunnerRegistration]>,
642    pub resolvers: Box<[ResolverRegistration]>,
643    pub debug_capabilities: Box<[DebugRegistration]>,
644    pub stop_timeout_ms: Option<u32>,
645}
646
647#[cfg(fuchsia_api_level_at_least = "31")]
648#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
649#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
650#[fidl_decl(fidl_table = "fdecl::DebugInfo")]
651pub struct DebugInfo {
652    pub manifest_sources: Option<Box<[String]>>,
653}
654
655#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
656#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
657#[fidl_decl(fidl_table = "fdecl::RunnerRegistration")]
658pub struct RunnerRegistration {
659    pub source_name: Name,
660    pub target_name: Name,
661    pub source: RegistrationSource,
662}
663
664impl SourceName for RunnerRegistration {
665    fn source_name(&self) -> &Name {
666        &self.source_name
667    }
668}
669
670impl RegistrationDeclCommon for RunnerRegistration {
671    const TYPE: &'static str = "runner";
672
673    fn source(&self) -> &RegistrationSource {
674        &self.source
675    }
676}
677
678#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
679#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
680#[fidl_decl(fidl_table = "fdecl::ResolverRegistration")]
681pub struct ResolverRegistration {
682    pub resolver: Name,
683    pub source: RegistrationSource,
684    pub scheme: String,
685}
686
687impl SourceName for ResolverRegistration {
688    fn source_name(&self) -> &Name {
689        &self.resolver
690    }
691}
692
693impl RegistrationDeclCommon for ResolverRegistration {
694    const TYPE: &'static str = "resolver";
695
696    fn source(&self) -> &RegistrationSource {
697        &self.source
698    }
699}
700
701#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
702#[fidl_decl(fidl_union = "fdecl::DebugRegistration")]
703pub enum DebugRegistration {
704    Protocol(DebugProtocolRegistration),
705}
706
707impl RegistrationDeclCommon for DebugRegistration {
708    const TYPE: &'static str = "debug_protocol";
709
710    fn source(&self) -> &RegistrationSource {
711        match self {
712            DebugRegistration::Protocol(protocol_reg) => &protocol_reg.source,
713        }
714    }
715}
716
717impl SourceName for DebugRegistration {
718    fn source_name(&self) -> &Name {
719        match self {
720            DebugRegistration::Protocol(protocol_reg) => &protocol_reg.source_name,
721        }
722    }
723}
724
725#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
726#[derive(FidlDecl, Debug, Clone, PartialEq, Eq)]
727#[fidl_decl(fidl_table = "fdecl::DebugProtocolRegistration")]
728pub struct DebugProtocolRegistration {
729    pub source_name: Name,
730    pub source: RegistrationSource,
731    pub target_name: Name,
732}
733
734#[derive(FidlDecl, Debug, Clone, PartialEq)]
735#[fidl_decl(fidl_table = "fdecl::Program")]
736pub struct ProgramDecl {
737    pub runner: Option<Name>,
738    pub info: fdata::Dictionary,
739}
740
741impl Default for ProgramDecl {
742    fn default() -> Self {
743        Self { runner: None, info: fdata::Dictionary::default() }
744    }
745}
746
747fidl_translations_identical!([u8; 32]);
748fidl_translations_identical!(u8);
749fidl_translations_identical!(u16);
750fidl_translations_identical!(u32);
751fidl_translations_identical!(u64);
752fidl_translations_identical!(i8);
753fidl_translations_identical!(i16);
754fidl_translations_identical!(i32);
755fidl_translations_identical!(i64);
756fidl_translations_identical!(bool);
757fidl_translations_identical!(String);
758fidl_translations_identical!(Vec<Name>);
759fidl_translations_identical!(fdecl::StartupMode);
760fidl_translations_identical!(fdecl::OnTerminate);
761fidl_translations_identical!(fdecl::Durability);
762fidl_translations_identical!(fdata::Dictionary);
763fidl_translations_identical!(fio::Operations);
764fidl_translations_identical!(fio::Flags);
765fidl_translations_identical!(fdecl::EnvironmentExtends);
766fidl_translations_identical!(fdecl::StorageId);
767fidl_translations_identical!(fsys::ServiceInstance);
768fidl_translations_from_into!(cm_types::AllowedOffers, fdecl::AllowedOffers);
769
770#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
771#[derive(Debug, Clone, PartialEq, Eq)]
772pub enum DependencyType {
773    Strong,
774    Weak,
775}
776
777impl Default for DependencyType {
778    fn default() -> Self {
779        Self::Strong
780    }
781}
782
783fidl_translations_symmetrical_enums!(fdecl::DependencyType, DependencyType, Strong, Weak);
784
785impl UseDecl {
786    pub fn path(&self) -> Option<&Path> {
787        match self {
788            UseDecl::Service(d) => Some(&d.target_path),
789            UseDecl::Protocol(d) => d.target_path.as_ref(),
790            UseDecl::Directory(d) => Some(&d.target_path),
791            UseDecl::Storage(d) => Some(&d.target_path),
792            UseDecl::EventStream(d) => Some(&d.target_path),
793            #[cfg(fuchsia_api_level_at_least = "HEAD")]
794            UseDecl::Runner(_) => None,
795            UseDecl::Config(_) => None,
796            #[cfg(fuchsia_api_level_at_least = "29")]
797            UseDecl::Dictionary(d) => Some(&d.target_path),
798        }
799    }
800
801    pub fn name(&self) -> Option<&Name> {
802        match self {
803            UseDecl::Storage(storage_decl) => Some(&storage_decl.source_name),
804            UseDecl::EventStream(_) => None,
805            UseDecl::Service(_) | UseDecl::Protocol(_) | UseDecl::Directory(_) => None,
806            #[cfg(fuchsia_api_level_at_least = "HEAD")]
807            UseDecl::Runner(_) => None,
808            UseDecl::Config(_) => None,
809            #[cfg(fuchsia_api_level_at_least = "29")]
810            UseDecl::Dictionary(_) => None,
811        }
812    }
813}
814
815impl SourceName for UseDecl {
816    fn source_name(&self) -> &Name {
817        match self {
818            UseDecl::Storage(storage_decl) => &storage_decl.source_name,
819            UseDecl::Service(service_decl) => &service_decl.source_name,
820            UseDecl::Protocol(protocol_decl) => &protocol_decl.source_name,
821            UseDecl::Directory(directory_decl) => &directory_decl.source_name,
822            UseDecl::EventStream(event_stream_decl) => &event_stream_decl.source_name,
823            #[cfg(fuchsia_api_level_at_least = "HEAD")]
824            UseDecl::Runner(runner_decl) => &runner_decl.source_name,
825            UseDecl::Config(u) => &u.source_name,
826            #[cfg(fuchsia_api_level_at_least = "29")]
827            UseDecl::Dictionary(dictionary_decl) => &dictionary_decl.source_name,
828        }
829    }
830}
831
832impl SourcePath for UseDecl {
833    fn source_path(&self) -> BorrowedSeparatedPath<'_> {
834        match self {
835            UseDecl::Service(u) => u.source_path(),
836            UseDecl::Protocol(u) => u.source_path(),
837            UseDecl::Directory(u) => u.source_path(),
838            UseDecl::Storage(u) => u.source_path(),
839            UseDecl::EventStream(u) => u.source_path(),
840            #[cfg(fuchsia_api_level_at_least = "HEAD")]
841            UseDecl::Runner(u) => u.source_path(),
842            UseDecl::Config(u) => u.source_path(),
843            #[cfg(fuchsia_api_level_at_least = "29")]
844            UseDecl::Dictionary(u) => u.source_path(),
845        }
846    }
847}
848
849/// The trait for all declarations that have a source name.
850pub trait SourceName {
851    fn source_name(&self) -> &Name;
852}
853
854/// The common properties of a Registration-with-environment declaration.
855pub trait RegistrationDeclCommon: SourceName + Send + Sync {
856    /// The name of the registration type, for error messages.
857    const TYPE: &'static str;
858    fn source(&self) -> &RegistrationSource;
859}
860
861/// The common properties of an [Expose](fdecl::Expose) declaration.
862pub trait ExposeDeclCommon: SourceName + SourcePath + fmt::Debug + Send + Sync {
863    fn target_name(&self) -> &Name;
864    fn target(&self) -> &ExposeTarget;
865    fn source(&self) -> &ExposeSource;
866    fn availability(&self) -> &Availability;
867}
868
869/// A named capability type.
870///
871/// `CapabilityTypeName` provides a user friendly type encoding for a capability.
872#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
873#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter)]
874pub enum CapabilityTypeName {
875    Directory,
876    EventStream,
877    Protocol,
878    Resolver,
879    Runner,
880    Service,
881    Storage,
882    Dictionary,
883    Config,
884}
885
886impl std::str::FromStr for CapabilityTypeName {
887    type Err = Error;
888
889    fn from_str(s: &str) -> Result<Self, Self::Err> {
890        match s {
891            "directory" => Ok(CapabilityTypeName::Directory),
892            "event_stream" => Ok(CapabilityTypeName::EventStream),
893            "protocol" => Ok(CapabilityTypeName::Protocol),
894            "resolver" => Ok(CapabilityTypeName::Resolver),
895            "runner" => Ok(CapabilityTypeName::Runner),
896            "service" => Ok(CapabilityTypeName::Service),
897            "storage" => Ok(CapabilityTypeName::Storage),
898            "dictionary" => Ok(CapabilityTypeName::Dictionary),
899            "configuration" => Ok(CapabilityTypeName::Config),
900            _ => Err(Error::ParseCapabilityTypeName { raw: s.to_string() }),
901        }
902    }
903}
904
905impl FidlIntoNative<CapabilityTypeName> for String {
906    fn fidl_into_native(self) -> CapabilityTypeName {
907        self.parse().unwrap()
908    }
909}
910
911impl NativeIntoFidl<String> for CapabilityTypeName {
912    fn native_into_fidl(self) -> String {
913        self.to_string()
914    }
915}
916
917impl AsRef<str> for CapabilityTypeName {
918    fn as_ref(&self) -> &str {
919        match self {
920            CapabilityTypeName::Directory => "directory",
921            CapabilityTypeName::EventStream => "event_stream",
922            CapabilityTypeName::Protocol => "protocol",
923            CapabilityTypeName::Resolver => "resolver",
924            CapabilityTypeName::Runner => "runner",
925            CapabilityTypeName::Service => "service",
926            CapabilityTypeName::Storage => "storage",
927            CapabilityTypeName::Dictionary => "dictionary",
928            CapabilityTypeName::Config => "configuration",
929        }
930    }
931}
932
933impl fmt::Display for CapabilityTypeName {
934    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
935        write!(f, "{}", self.as_ref())
936    }
937}
938
939impl From<&UseDecl> for CapabilityTypeName {
940    fn from(use_decl: &UseDecl) -> Self {
941        match use_decl {
942            UseDecl::Service(_) => Self::Service,
943            UseDecl::Protocol(_) => Self::Protocol,
944            UseDecl::Directory(_) => Self::Directory,
945            UseDecl::Storage(_) => Self::Storage,
946            UseDecl::EventStream(_) => Self::EventStream,
947            #[cfg(fuchsia_api_level_at_least = "HEAD")]
948            UseDecl::Runner(_) => Self::Runner,
949            UseDecl::Config(_) => Self::Config,
950            #[cfg(fuchsia_api_level_at_least = "29")]
951            UseDecl::Dictionary(_) => Self::Dictionary,
952        }
953    }
954}
955
956impl From<&ExposeDecl> for CapabilityTypeName {
957    fn from(expose_decl: &ExposeDecl) -> Self {
958        match expose_decl {
959            ExposeDecl::Service(_) => Self::Service,
960            ExposeDecl::Protocol(_) => Self::Protocol,
961            ExposeDecl::Directory(_) => Self::Directory,
962            ExposeDecl::Runner(_) => Self::Runner,
963            ExposeDecl::Resolver(_) => Self::Resolver,
964            ExposeDecl::Dictionary(_) => Self::Dictionary,
965            ExposeDecl::Config(_) => Self::Config,
966        }
967    }
968}
969
970impl From<&DebugRegistration> for CapabilityTypeName {
971    fn from(debug: &DebugRegistration) -> Self {
972        match debug {
973            DebugRegistration::Protocol(_) => Self::Protocol,
974        }
975    }
976}
977
978impl From<&RunnerRegistration> for CapabilityTypeName {
979    fn from(_: &RunnerRegistration) -> Self {
980        Self::Runner
981    }
982}
983
984impl From<&ResolverRegistration> for CapabilityTypeName {
985    fn from(_: &ResolverRegistration) -> Self {
986        Self::Resolver
987    }
988}
989
990impl From<CapabilityTypeName> for fio::DirentType {
991    fn from(value: CapabilityTypeName) -> Self {
992        match value {
993            CapabilityTypeName::Directory => fio::DirentType::Directory,
994            CapabilityTypeName::EventStream => fio::DirentType::Service,
995            CapabilityTypeName::Protocol => fio::DirentType::Service,
996            CapabilityTypeName::Service => fio::DirentType::Directory,
997            CapabilityTypeName::Storage => fio::DirentType::Directory,
998            CapabilityTypeName::Dictionary => fio::DirentType::Directory,
999            CapabilityTypeName::Resolver => fio::DirentType::Service,
1000            CapabilityTypeName::Runner => fio::DirentType::Service,
1001            // Config capabilities don't appear in exposed or used dir
1002            CapabilityTypeName::Config => fio::DirentType::Unknown,
1003        }
1004    }
1005}
1006
1007// TODO: Runners and third parties can use this to parse `facets`.
1008impl FidlIntoNative<HashMap<String, DictionaryValue>> for fdata::Dictionary {
1009    fn fidl_into_native(self) -> HashMap<String, DictionaryValue> {
1010        from_fidl_dict(self)
1011    }
1012}
1013
1014impl NativeIntoFidl<fdata::Dictionary> for HashMap<String, DictionaryValue> {
1015    fn native_into_fidl(self) -> fdata::Dictionary {
1016        to_fidl_dict(self)
1017    }
1018}
1019
1020impl FidlIntoNative<BTreeMap<String, DictionaryValue>> for fdata::Dictionary {
1021    fn fidl_into_native(self) -> BTreeMap<String, DictionaryValue> {
1022        from_fidl_dict_btree(self)
1023    }
1024}
1025
1026impl NativeIntoFidl<fdata::Dictionary> for BTreeMap<String, DictionaryValue> {
1027    fn native_into_fidl(self) -> fdata::Dictionary {
1028        to_fidl_dict_btree(self)
1029    }
1030}
1031
1032#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1033pub enum DictionaryValue {
1034    Str(String),
1035    StrVec(Vec<String>),
1036    Null,
1037}
1038
1039impl FidlIntoNative<DictionaryValue> for Option<Box<fdata::DictionaryValue>> {
1040    fn fidl_into_native(self) -> DictionaryValue {
1041        match self {
1042            Some(v) => match *v {
1043                fdata::DictionaryValue::Str(s) => DictionaryValue::Str(s),
1044                fdata::DictionaryValue::StrVec(ss) => DictionaryValue::StrVec(ss),
1045                _ => DictionaryValue::Null,
1046            },
1047            None => DictionaryValue::Null,
1048        }
1049    }
1050}
1051
1052impl NativeIntoFidl<Option<Box<fdata::DictionaryValue>>> for DictionaryValue {
1053    fn native_into_fidl(self) -> Option<Box<fdata::DictionaryValue>> {
1054        match self {
1055            DictionaryValue::Str(s) => Some(Box::new(fdata::DictionaryValue::Str(s))),
1056            DictionaryValue::StrVec(ss) => Some(Box::new(fdata::DictionaryValue::StrVec(ss))),
1057            DictionaryValue::Null => None,
1058        }
1059    }
1060}
1061
1062fn from_fidl_dict(dict: fdata::Dictionary) -> HashMap<String, DictionaryValue> {
1063    match dict.entries {
1064        Some(entries) => entries.into_iter().map(|e| (e.key, e.value.fidl_into_native())).collect(),
1065        _ => HashMap::new(),
1066    }
1067}
1068
1069fn to_fidl_dict(dict: HashMap<String, DictionaryValue>) -> fdata::Dictionary {
1070    fdata::Dictionary {
1071        entries: Some(
1072            dict.into_iter()
1073                .map(|(key, value)| fdata::DictionaryEntry { key, value: value.native_into_fidl() })
1074                .collect(),
1075        ),
1076        ..Default::default()
1077    }
1078}
1079
1080fn from_fidl_dict_btree(dict: fdata::Dictionary) -> BTreeMap<String, DictionaryValue> {
1081    match dict.entries {
1082        Some(entries) => entries.into_iter().map(|e| (e.key, e.value.fidl_into_native())).collect(),
1083        _ => BTreeMap::new(),
1084    }
1085}
1086
1087fn to_fidl_dict_btree(dict: BTreeMap<String, DictionaryValue>) -> fdata::Dictionary {
1088    fdata::Dictionary {
1089        entries: Some(
1090            dict.into_iter()
1091                .map(|(key, value)| fdata::DictionaryEntry { key, value: value.native_into_fidl() })
1092                .collect(),
1093        ),
1094        ..Default::default()
1095    }
1096}
1097
1098#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1099#[derive(Debug, Clone, PartialEq, Eq, Hash, Error)]
1100pub enum GenericRef {
1101    Parent,
1102    Self_,
1103    Child(Name),
1104    ChildInCollection(LongName, Name),
1105    Collection(Name),
1106    Framework,
1107    Capability(Name),
1108    Debug,
1109    Void,
1110    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1111    Environment,
1112}
1113
1114impl std::fmt::Display for GenericRef {
1115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1116        match self {
1117            Self::Parent => write!(f, "parent"),
1118            Self::Self_ => write!(f, "self"),
1119            Self::Child(name) => write!(f, "child {name}"),
1120            Self::ChildInCollection(name, collection) => {
1121                write!(f, "child {name} in collection {collection}")
1122            }
1123            Self::Collection(name) => write!(f, "collection {name}"),
1124            Self::Framework => write!(f, "framework"),
1125            Self::Capability(name) => write!(f, "capability {name}"),
1126            Self::Debug => write!(f, "debug"),
1127            Self::Void => write!(f, "void"),
1128            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1129            Self::Environment => write!(f, "environment"),
1130        }
1131    }
1132}
1133
1134pub trait ToGenericRef {
1135    fn to_generic(self) -> GenericRef;
1136}
1137
1138impl ToGenericRef for ChildRef {
1139    fn to_generic(self) -> GenericRef {
1140        if let Some(collection) = self.collection {
1141            GenericRef::ChildInCollection(self.name, collection)
1142        } else {
1143            GenericRef::Child(self.name.to_short_infallible())
1144        }
1145    }
1146}
1147
1148impl FidlIntoNative<GenericRef> for fdecl::Ref {
1149    fn fidl_into_native(self) -> GenericRef {
1150        match self {
1151            fdecl::Ref::Parent(_) => GenericRef::Parent,
1152            fdecl::Ref::Self_(_) => GenericRef::Self_,
1153            fdecl::Ref::Child(child_ref) => {
1154                let child_ref: ChildRef = child_ref.fidl_into_native();
1155                child_ref.to_generic()
1156            }
1157            fdecl::Ref::Collection(collection) => {
1158                GenericRef::Collection(collection.name.fidl_into_native())
1159            }
1160            fdecl::Ref::Framework(_) => GenericRef::Framework,
1161            fdecl::Ref::Capability(capability) => {
1162                GenericRef::Capability(capability.name.fidl_into_native())
1163            }
1164            fdecl::Ref::Debug(_) => GenericRef::Debug,
1165            fdecl::Ref::VoidType(_) => GenericRef::Void,
1166            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1167            fdecl::Ref::Environment(_) => GenericRef::Environment,
1168            _ => panic!("unexpected fdecl::Ref variant found"),
1169        }
1170    }
1171}
1172
1173impl NativeIntoFidl<fdecl::Ref> for GenericRef {
1174    fn native_into_fidl(self) -> fdecl::Ref {
1175        match self {
1176            GenericRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
1177            GenericRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
1178            GenericRef::Child(name) => fdecl::Ref::Child(fdecl::ChildRef {
1179                name: name.native_into_fidl(),
1180                collection: None,
1181            }),
1182            GenericRef::ChildInCollection(name, collection) => fdecl::Ref::Child(fdecl::ChildRef {
1183                name: name.native_into_fidl(),
1184                collection: Some(collection.native_into_fidl()),
1185            }),
1186            GenericRef::Collection(name) => {
1187                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.native_into_fidl() })
1188            }
1189            GenericRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
1190            GenericRef::Capability(name) => {
1191                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.native_into_fidl() })
1192            }
1193            GenericRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
1194            GenericRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
1195            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1196            GenericRef::Environment => fdecl::Ref::Environment(fdecl::EnvironmentRef {}),
1197        }
1198    }
1199}
1200
1201#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1202#[derive(Debug, Clone, PartialEq, Eq, Hash, ToGenericRef)]
1203pub enum EventScope {
1204    Child(ChildRef),
1205    Collection(Name),
1206}
1207
1208impl FidlIntoNative<EventScope> for fdecl::Ref {
1209    fn fidl_into_native(self) -> EventScope {
1210        match self {
1211            fdecl::Ref::Child(c) => {
1212                if let Some(_) = c.collection {
1213                    panic!("Dynamic children scopes are not supported for EventStreams");
1214                } else {
1215                    EventScope::Child(ChildRef { name: c.name.parse().unwrap(), collection: None })
1216                }
1217            }
1218            fdecl::Ref::Collection(collection) => {
1219                // cm_fidl_validator should have already validated this
1220                EventScope::Collection(collection.name.parse().unwrap())
1221            }
1222            _ => panic!("invalid EventScope variant"),
1223        }
1224    }
1225}
1226
1227#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1228#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1229pub enum ExposeSource {
1230    Self_,
1231    Child(Name),
1232    Collection(Name),
1233    Framework,
1234    Capability(Name),
1235    Void,
1236}
1237
1238impl std::fmt::Display for ExposeSource {
1239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1240        match self {
1241            Self::Framework => write!(f, "framework"),
1242            Self::Child(c) => write!(f, "child `#{}`", c),
1243            Self::Collection(c) => write!(f, "collection `#{}`", c),
1244            Self::Self_ => write!(f, "self"),
1245            Self::Capability(c) => write!(f, "capability `{}`", c),
1246            Self::Void => write!(f, "void"),
1247        }
1248    }
1249}
1250
1251impl FidlIntoNative<ExposeSource> for fdecl::Ref {
1252    fn fidl_into_native(self) -> ExposeSource {
1253        match self {
1254            fdecl::Ref::Self_(_) => ExposeSource::Self_,
1255            // cm_fidl_validator should have already validated this
1256            fdecl::Ref::Child(c) => ExposeSource::Child(c.name.parse().unwrap()),
1257            // cm_fidl_validator should have already validated this
1258            fdecl::Ref::Collection(c) => ExposeSource::Collection(c.name.parse().unwrap()),
1259            fdecl::Ref::Framework(_) => ExposeSource::Framework,
1260            // cm_fidl_validator should have already validated this
1261            fdecl::Ref::Capability(c) => ExposeSource::Capability(c.name.parse().unwrap()),
1262            fdecl::Ref::VoidType(_) => ExposeSource::Void,
1263            _ => panic!("invalid ExposeSource variant"),
1264        }
1265    }
1266}
1267
1268#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1269#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, ToGenericRef)]
1270pub enum ExposeTarget {
1271    Parent,
1272    Framework,
1273}
1274
1275impl std::fmt::Display for ExposeTarget {
1276    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1277        match self {
1278            Self::Framework => write!(f, "framework"),
1279            Self::Parent => write!(f, "parent"),
1280        }
1281    }
1282}
1283
1284impl FidlIntoNative<ExposeTarget> for fdecl::Ref {
1285    fn fidl_into_native(self) -> ExposeTarget {
1286        match self {
1287            fdecl::Ref::Parent(_) => ExposeTarget::Parent,
1288            fdecl::Ref::Framework(_) => ExposeTarget::Framework,
1289            _ => panic!("invalid ExposeTarget variant"),
1290        }
1291    }
1292}
1293
1294/// A source for a service.
1295#[derive(Debug, Clone, PartialEq, Eq)]
1296pub struct ServiceSource<T> {
1297    /// The provider of the service, relative to a component.
1298    pub source: T,
1299    /// The name of the service.
1300    pub source_name: Name,
1301}
1302
1303#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1304#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1305pub enum StorageDirectorySource {
1306    Parent,
1307    Self_,
1308    Child(String),
1309}
1310
1311impl FidlIntoNative<StorageDirectorySource> for fdecl::Ref {
1312    fn fidl_into_native(self) -> StorageDirectorySource {
1313        match self {
1314            fdecl::Ref::Parent(_) => StorageDirectorySource::Parent,
1315            fdecl::Ref::Self_(_) => StorageDirectorySource::Self_,
1316            fdecl::Ref::Child(c) => StorageDirectorySource::Child(c.name),
1317            _ => panic!("invalid StorageDirectorySource variant"),
1318        }
1319    }
1320}
1321
1322#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1323#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1324pub enum DictionarySource {
1325    Parent,
1326    Self_,
1327    Child(ChildRef),
1328}
1329
1330impl FidlIntoNative<DictionarySource> for fdecl::Ref {
1331    fn fidl_into_native(self) -> DictionarySource {
1332        match self {
1333            Self::Parent(_) => DictionarySource::Parent,
1334            Self::Self_(_) => DictionarySource::Self_,
1335            Self::Child(c) => DictionarySource::Child(c.fidl_into_native()),
1336            _ => panic!("invalid DictionarySource variant"),
1337        }
1338    }
1339}
1340
1341#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1342#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1343pub enum RegistrationSource {
1344    Parent,
1345    Self_,
1346    Child(String),
1347}
1348
1349impl FidlIntoNative<RegistrationSource> for fdecl::Ref {
1350    fn fidl_into_native(self) -> RegistrationSource {
1351        match self {
1352            fdecl::Ref::Parent(_) => RegistrationSource::Parent,
1353            fdecl::Ref::Self_(_) => RegistrationSource::Self_,
1354            fdecl::Ref::Child(c) => RegistrationSource::Child(c.name),
1355            _ => panic!("invalid RegistrationSource variant"),
1356        }
1357    }
1358}
1359
1360/// Converts the contents of a CM-FIDL declaration and produces the equivalent CM-Rust
1361/// struct.
1362/// This function applies cm_fidl_validator to check correctness.
1363impl TryFrom<fdecl::Component> for ComponentDecl {
1364    type Error = Error;
1365
1366    fn try_from(decl: fdecl::Component) -> Result<Self, Self::Error> {
1367        cm_fidl_validator::validate(&decl, &mut DirectedGraph::new())
1368            .map_err(|err| Error::Validate { err })?;
1369        Ok(decl.fidl_into_native())
1370    }
1371}
1372
1373// Converts the contents of a CM-Rust declaration into a CM_FIDL declaration
1374impl From<ComponentDecl> for fdecl::Component {
1375    fn from(decl: ComponentDecl) -> Self {
1376        decl.native_into_fidl()
1377    }
1378}
1379
1380/// Errors produced by cm_rust.
1381#[derive(Debug, Error, Clone)]
1382pub enum Error {
1383    #[error("Fidl validation failed: {}", err)]
1384    Validate {
1385        #[source]
1386        err: cm_fidl_validator::error::ErrorList,
1387    },
1388    #[error("Invalid capability path: {}", raw)]
1389    InvalidCapabilityPath { raw: String },
1390    #[error("Invalid capability type name: {}", raw)]
1391    ParseCapabilityTypeName { raw: String },
1392}
1393
1394/// Push `value` onto the end of `Box<[T]>`. Convenience function for clients that work with
1395/// cm_rust, which uses `Box<[T]>` instead of `Vec<T>` for its list type.
1396pub fn push_box<T>(container: &mut Box<[T]>, value: T) {
1397    let boxed = mem::replace(container, Box::from([]));
1398    let mut new_container: Vec<_> = boxed.into();
1399    new_container.push(value);
1400    *container = new_container.into();
1401}
1402
1403/// Append `other` to the end of `Box<[T]>`. Convenience function for clients that work with
1404/// cm_rust, which uses `Box<[T]>` instead of `Vec<T>` for its list type.
1405pub fn append_box<T>(container: &mut Box<[T]>, other: &mut Vec<T>) {
1406    let boxed = mem::replace(container, Box::from([]));
1407    let mut new_container: Vec<_> = boxed.into();
1408    new_container.append(other);
1409    *container = new_container.into();
1410}
1411
1412#[cfg(test)]
1413mod tests {
1414    use super::*;
1415    use difference::Changeset;
1416    use fidl_fuchsia_component_decl_common as fdecl;
1417
1418    fn offer_source_static_child(name: &str) -> OfferSource {
1419        OfferSource::Child(ChildRef { name: name.parse().unwrap(), collection: None })
1420    }
1421
1422    fn offer_target_static_child(name: &str) -> OfferTarget {
1423        OfferTarget::Child(ChildRef { name: name.parse().unwrap(), collection: None })
1424    }
1425
1426    macro_rules! test_try_from_decl {
1427        (
1428            $(
1429                $test_name:ident => {
1430                    input = $input:expr,
1431                    result = $result:expr,
1432                },
1433            )+
1434        ) => {
1435            $(
1436                #[test]
1437                fn $test_name() {
1438                    {
1439                        let res = ComponentDecl::try_from($input).expect("try_from failed");
1440                        if res != $result {
1441                            let a = format!("{:#?}", res);
1442                            let e = format!("{:#?}", $result);
1443                            panic!("Conversion from fidl to cm_rust did not yield expected result:\n{}", Changeset::new(&a, &e, "\n"));
1444                        }
1445                    }
1446                    {
1447                        let res = fdecl::Component::try_from($result).expect("try_from failed");
1448                        if res != $input {
1449                            let a = format!("{:#?}", res);
1450                            let e = format!("{:#?}", $input);
1451                            panic!("Conversion from cm_rust to fidl did not yield expected result:\n{}", Changeset::new(&a, &e, "\n"));
1452                        }
1453                    }
1454                }
1455            )+
1456        }
1457    }
1458
1459    macro_rules! test_fidl_into_and_from {
1460        (
1461            $(
1462                $test_name:ident => {
1463                    input = $input:expr,
1464                    input_type = $input_type:ty,
1465                    result = $result:expr,
1466                    result_type = $result_type:ty,
1467                },
1468            )+
1469        ) => {
1470            $(
1471                #[test]
1472                fn $test_name() {
1473                    {
1474                        let res: Vec<$result_type> =
1475                            $input.into_iter().map(|e| e.fidl_into_native()).collect();
1476                        assert_eq!(res, $result);
1477                    }
1478                    {
1479                        let res: Vec<$input_type> =
1480                            $result.into_iter().map(|e| e.native_into_fidl()).collect();
1481                        assert_eq!(res, $input);
1482                    }
1483                }
1484            )+
1485        }
1486    }
1487
1488    macro_rules! test_fidl_into {
1489        (
1490            $(
1491                $test_name:ident => {
1492                    input = $input:expr,
1493                    result = $result:expr,
1494                },
1495            )+
1496        ) => {
1497            $(
1498                #[test]
1499                fn $test_name() {
1500                    test_fidl_into_helper($input, $result);
1501                }
1502            )+
1503        }
1504    }
1505
1506    fn test_fidl_into_helper<T, U>(input: T, expected_res: U)
1507    where
1508        T: FidlIntoNative<U>,
1509        U: std::cmp::PartialEq + std::fmt::Debug,
1510    {
1511        let res: U = input.fidl_into_native();
1512        assert_eq!(res, expected_res);
1513    }
1514
1515    test_try_from_decl! {
1516        try_from_empty => {
1517            input = fdecl::Component {
1518                program: None,
1519                uses: None,
1520                exposes: None,
1521                offers: None,
1522                capabilities: None,
1523                children: None,
1524                collections: None,
1525                facets: None,
1526                environments: None,
1527                ..Default::default()
1528            },
1529            result = ComponentDecl {
1530                program: None,
1531                uses: Box::from([]),
1532                exposes: Box::from([]),
1533                offers: Box::from([]),
1534                capabilities: Box::from([]),
1535                children: Box::from([]),
1536                collections: Box::from([]),
1537                facets: None,
1538                environments: Box::from([]),
1539                config: None,
1540                debug_info: None,
1541            },
1542        },
1543        try_from_all => {
1544            input = fdecl::Component {
1545                program: Some(fdecl::Program {
1546                    runner: Some("elf".to_string()),
1547                    info: Some(fdata::Dictionary {
1548                        entries: Some(vec![
1549                            fdata::DictionaryEntry {
1550                                key: "args".to_string(),
1551                                value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["foo".to_string(), "bar".to_string()]))),
1552                            },
1553                            fdata::DictionaryEntry {
1554                                key: "binary".to_string(),
1555                                value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
1556                            },
1557                        ]),
1558                        ..Default::default()
1559                    }),
1560                    ..Default::default()
1561                }),
1562                uses: Some(vec![
1563                    fdecl::Use::Service(fdecl::UseService {
1564                        dependency_type: Some(fdecl::DependencyType::Strong),
1565                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1566                        source_name: Some("netstack".to_string()),
1567                        source_dictionary: Some("in/dict".to_string()),
1568                        target_path: Some("/svc/mynetstack".to_string()),
1569                        availability: Some(fdecl::Availability::Required),
1570                        ..Default::default()
1571                    }),
1572                    fdecl::Use::Protocol(fdecl::UseProtocol {
1573                        dependency_type: Some(fdecl::DependencyType::Strong),
1574                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1575                        source_name: Some("legacy_netstack".to_string()),
1576                        source_dictionary: Some("in/dict".to_string()),
1577                        target_path: None,
1578                        numbered_handle: Some(0xab),
1579                        availability: Some(fdecl::Availability::Optional),
1580                        ..Default::default()
1581                    }),
1582                    fdecl::Use::Protocol(fdecl::UseProtocol {
1583                        dependency_type: Some(fdecl::DependencyType::Strong),
1584                        source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "echo".to_string(), collection: None})),
1585                        source_name: Some("echo_service".to_string()),
1586                        source_dictionary: Some("in/dict".to_string()),
1587                        target_path: Some("/svc/echo_service".to_string()),
1588                        availability: Some(fdecl::Availability::Required),
1589                        ..Default::default()
1590                    }),
1591                    fdecl::Use::Directory(fdecl::UseDirectory {
1592                        dependency_type: Some(fdecl::DependencyType::Strong),
1593                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
1594                        source_name: Some("dir".to_string()),
1595                        source_dictionary: Some("dict1/me".to_string()),
1596                        target_path: Some("/data".to_string()),
1597                        rights: Some(fio::Operations::CONNECT),
1598                        subdir: Some("foo/bar".to_string()),
1599                        availability: Some(fdecl::Availability::Required),
1600                        ..Default::default()
1601                    }),
1602                    fdecl::Use::Storage(fdecl::UseStorage {
1603                        source_name: Some("cache".to_string()),
1604                        target_path: Some("/cache".to_string()),
1605                        availability: Some(fdecl::Availability::Required),
1606                        ..Default::default()
1607                    }),
1608                    fdecl::Use::Storage(fdecl::UseStorage {
1609                        source_name: Some("temp".to_string()),
1610                        target_path: Some("/temp".to_string()),
1611                        availability: Some(fdecl::Availability::Optional),
1612                        ..Default::default()
1613                    }),
1614                    fdecl::Use::EventStream(fdecl::UseEventStream {
1615                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1616                            collection: None,
1617                            name: "netstack".to_string(),
1618                        })),
1619                        source_name: Some("stopped".to_string()),
1620                        scope: Some(vec![
1621                            fdecl::Ref::Child(fdecl::ChildRef {
1622                                collection: None,
1623                                name:"a".to_string(),
1624                        }), fdecl::Ref::Collection(fdecl::CollectionRef {
1625                            name:"b".to_string(),
1626                        })]),
1627                        target_path: Some("/svc/test".to_string()),
1628                        availability: Some(fdecl::Availability::Optional),
1629                        ..Default::default()
1630                    }),
1631                    fdecl::Use::Runner(fdecl::UseRunner {
1632                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
1633                        source_name: Some("elf".to_string()),
1634                        source_dictionary: None,
1635                        ..Default::default()
1636                    }),
1637                    fdecl::Use::Config(fdecl::UseConfiguration {
1638                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
1639                        source_name: Some("fuchsia.config.MyConfig".to_string()),
1640                        target_name: Some("my_config".to_string()),
1641                        availability: Some(fdecl::Availability::Required),
1642                        type_: Some(fdecl::ConfigType{
1643                            layout: fdecl::ConfigTypeLayout::Bool,
1644                            parameters: Some(Vec::new()),
1645                            constraints: Vec::new(),
1646                        }),
1647                        ..Default::default()
1648                    }),
1649                    #[cfg(fuchsia_api_level_at_least = "29")]
1650                    fdecl::Use::Dictionary(fdecl::UseDictionary {
1651                        dependency_type: Some(fdecl::DependencyType::Strong),
1652                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1653                        source_name: Some("dictionary".to_string()),
1654                        source_dictionary: Some("other_dictionary".to_string()),
1655                        target_path: Some("/svc".to_string()),
1656                        availability: Some(fdecl::Availability::Optional),
1657                        ..Default::default()
1658                    }),
1659                ]),
1660                exposes: Some(vec![
1661                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
1662                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1663                            name: "netstack".to_string(),
1664                            collection: None,
1665                        })),
1666                        source_name: Some("legacy_netstack".to_string()),
1667                        source_dictionary: Some("in/dict".to_string()),
1668                        target_name: Some("legacy_mynetstack".to_string()),
1669                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1670                        availability: Some(fdecl::Availability::Required),
1671                        ..Default::default()
1672                    }),
1673                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
1674                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1675                            name: "netstack".to_string(),
1676                            collection: None,
1677                        })),
1678                        source_name: Some("dir".to_string()),
1679                        source_dictionary: Some("in/dict".to_string()),
1680                        target_name: Some("data".to_string()),
1681                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1682                        rights: Some(fio::Operations::CONNECT),
1683                        subdir: Some("foo/bar".to_string()),
1684                        availability: Some(fdecl::Availability::Optional),
1685                        ..Default::default()
1686                    }),
1687                    fdecl::Expose::Runner(fdecl::ExposeRunner {
1688                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1689                            name: "netstack".to_string(),
1690                            collection: None,
1691                        })),
1692                        source_name: Some("elf".to_string()),
1693                        source_dictionary: Some("in/dict".to_string()),
1694                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1695                        target_name: Some("elf".to_string()),
1696                        ..Default::default()
1697                    }),
1698                    fdecl::Expose::Resolver(fdecl::ExposeResolver{
1699                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1700                            name: "netstack".to_string(),
1701                            collection: None,
1702                        })),
1703                        source_name: Some("pkg".to_string()),
1704                        source_dictionary: Some("in/dict".to_string()),
1705                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
1706                        target_name: Some("pkg".to_string()),
1707                        ..Default::default()
1708                    }),
1709                    fdecl::Expose::Service(fdecl::ExposeService {
1710                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1711                            name: "netstack".to_string(),
1712                            collection: None,
1713                        })),
1714                        source_name: Some("netstack1".to_string()),
1715                        source_dictionary: Some("in/dict".to_string()),
1716                        target_name: Some("mynetstack".to_string()),
1717                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1718                        availability: Some(fdecl::Availability::Required),
1719                        ..Default::default()
1720                    }),
1721                    fdecl::Expose::Service(fdecl::ExposeService {
1722                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
1723                            name: "modular".to_string(),
1724                        })),
1725                        source_name: Some("netstack2".to_string()),
1726                        source_dictionary: None,
1727                        target_name: Some("mynetstack".to_string()),
1728                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1729                        availability: Some(fdecl::Availability::Required),
1730                        ..Default::default()
1731                    }),
1732                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
1733                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1734                            name: "netstack".to_string(),
1735                            collection: None,
1736                        })),
1737                        source_name: Some("bundle".to_string()),
1738                        source_dictionary: Some("in/dict".to_string()),
1739                        target_name: Some("mybundle".to_string()),
1740                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1741                        availability: Some(fdecl::Availability::Required),
1742                        ..Default::default()
1743                    }),
1744                ]),
1745                offers: Some(vec![
1746                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
1747                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1748                        source_name: Some("legacy_netstack".to_string()),
1749                        source_dictionary: Some("in/dict".to_string()),
1750                        target: Some(fdecl::Ref::Child(
1751                           fdecl::ChildRef {
1752                               name: "echo".to_string(),
1753                               collection: None,
1754                           }
1755                        )),
1756                        target_name: Some("legacy_mynetstack".to_string()),
1757                        dependency_type: Some(fdecl::DependencyType::Weak),
1758                        availability: Some(fdecl::Availability::Required),
1759                        ..Default::default()
1760                    }),
1761                    fdecl::Offer::Directory(fdecl::OfferDirectory {
1762                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1763                        source_name: Some("dir".to_string()),
1764                        source_dictionary: Some("in/dict".to_string()),
1765                        target: Some(fdecl::Ref::Collection(
1766                            fdecl::CollectionRef { name: "modular".to_string() }
1767                        )),
1768                        target_name: Some("data".to_string()),
1769                        rights: Some(fio::Operations::CONNECT),
1770                        subdir: None,
1771                        dependency_type: Some(fdecl::DependencyType::Strong),
1772                        availability: Some(fdecl::Availability::Optional),
1773                        ..Default::default()
1774                    }),
1775                    fdecl::Offer::Storage(fdecl::OfferStorage {
1776                        source_name: Some("cache".to_string()),
1777                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
1778                        target: Some(fdecl::Ref::Collection(
1779                            fdecl::CollectionRef { name: "modular".to_string() }
1780                        )),
1781                        target_name: Some("cache".to_string()),
1782                        availability: Some(fdecl::Availability::Required),
1783                        ..Default::default()
1784                    }),
1785                    fdecl::Offer::Runner(fdecl::OfferRunner {
1786                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1787                        source_name: Some("elf".to_string()),
1788                        source_dictionary: Some("in/dict".to_string()),
1789                        target: Some(fdecl::Ref::Child(
1790                           fdecl::ChildRef {
1791                               name: "echo".to_string(),
1792                               collection: None,
1793                           }
1794                        )),
1795                        target_name: Some("elf2".to_string()),
1796                        ..Default::default()
1797                    }),
1798                    fdecl::Offer::Resolver(fdecl::OfferResolver{
1799                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
1800                        source_name: Some("pkg".to_string()),
1801                        source_dictionary: Some("in/dict".to_string()),
1802                        target: Some(fdecl::Ref::Child(
1803                           fdecl::ChildRef {
1804                              name: "echo".to_string(),
1805                              collection: None,
1806                           }
1807                        )),
1808                        target_name: Some("pkg".to_string()),
1809                        ..Default::default()
1810                    }),
1811                    fdecl::Offer::Service(fdecl::OfferService {
1812                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1813                        source_name: Some("netstack1".to_string()),
1814                        source_dictionary: Some("in/dict".to_string()),
1815                        target: Some(fdecl::Ref::Child(
1816                           fdecl::ChildRef {
1817                               name: "echo".to_string(),
1818                               collection: None,
1819                           }
1820                        )),
1821                        target_name: Some("mynetstack1".to_string()),
1822                        availability: Some(fdecl::Availability::Required),
1823                        dependency_type: Some(fdecl::DependencyType::Strong),
1824                        ..Default::default()
1825                    }),
1826                    fdecl::Offer::Service(fdecl::OfferService {
1827                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1828                        source_name: Some("netstack2".to_string()),
1829                        source_dictionary: None,
1830                        target: Some(fdecl::Ref::Child(
1831                           fdecl::ChildRef {
1832                               name: "echo".to_string(),
1833                               collection: None,
1834                           }
1835                        )),
1836                        target_name: Some("mynetstack2".to_string()),
1837                        availability: Some(fdecl::Availability::Optional),
1838                        dependency_type: Some(fdecl::DependencyType::Strong),
1839                        ..Default::default()
1840                    }),
1841                    fdecl::Offer::Service(fdecl::OfferService {
1842                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1843                        source_name: Some("netstack3".to_string()),
1844                        source_dictionary: None,
1845                        target: Some(fdecl::Ref::Child(
1846                           fdecl::ChildRef {
1847                               name: "echo".to_string(),
1848                               collection: None,
1849                           }
1850                        )),
1851                        target_name: Some("mynetstack3".to_string()),
1852                        source_instance_filter: Some(vec!["allowedinstance".to_string()]),
1853                        renamed_instances: Some(vec![fdecl::NameMapping{source_name: "default".to_string(), target_name: "allowedinstance".to_string()}]),
1854                        availability: Some(fdecl::Availability::Required),
1855                        dependency_type: Some(fdecl::DependencyType::Strong),
1856                        ..Default::default()
1857                    }),
1858                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
1859                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1860                        source_name: Some("bundle".to_string()),
1861                        source_dictionary: Some("in/dict".to_string()),
1862                        target: Some(fdecl::Ref::Child(
1863                           fdecl::ChildRef {
1864                               name: "echo".to_string(),
1865                               collection: None,
1866                           }
1867                        )),
1868                        target_name: Some("mybundle".to_string()),
1869                        dependency_type: Some(fdecl::DependencyType::Weak),
1870                        availability: Some(fdecl::Availability::Required),
1871                        ..Default::default()
1872                    }),
1873                ]),
1874                capabilities: Some(vec![
1875                    fdecl::Capability::Service(fdecl::Service {
1876                        name: Some("netstack".to_string()),
1877                        source_path: Some("/netstack".to_string()),
1878                        ..Default::default()
1879                    }),
1880                    fdecl::Capability::Protocol(fdecl::Protocol {
1881                        name: Some("netstack2".to_string()),
1882                        source_path: Some("/netstack2".to_string()),
1883                        delivery: Some(fdecl::DeliveryType::Immediate),
1884                        ..Default::default()
1885                    }),
1886                    fdecl::Capability::Directory(fdecl::Directory {
1887                        name: Some("data".to_string()),
1888                        source_path: Some("/data".to_string()),
1889                        rights: Some(fio::Operations::CONNECT),
1890                        ..Default::default()
1891                    }),
1892                    fdecl::Capability::Storage(fdecl::Storage {
1893                        name: Some("cache".to_string()),
1894                        backing_dir: Some("data".to_string()),
1895                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1896                        subdir: Some("cache".to_string()),
1897                        storage_id: Some(fdecl::StorageId::StaticInstanceId),
1898                        ..Default::default()
1899                    }),
1900                    fdecl::Capability::Runner(fdecl::Runner {
1901                        name: Some("elf".to_string()),
1902                        source_path: Some("/elf".to_string()),
1903                        ..Default::default()
1904                    }),
1905                    fdecl::Capability::Resolver(fdecl::Resolver {
1906                        name: Some("pkg".to_string()),
1907                        source_path: Some("/pkg_resolver".to_string()),
1908                        ..Default::default()
1909                    }),
1910                    fdecl::Capability::Dictionary(fdecl::Dictionary {
1911                        name: Some("dict1".to_string()),
1912                        ..Default::default()
1913                    }),
1914                    fdecl::Capability::Dictionary(fdecl::Dictionary {
1915                        name: Some("dict2".to_string()),
1916                        source_path: Some("/in/other".to_string()),
1917                        ..Default::default()
1918                    }),
1919                ]),
1920                children: Some(vec![
1921                     fdecl::Child {
1922                         name: Some("netstack".to_string()),
1923                         url: Some("fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cm"
1924                                   .to_string()),
1925                         startup: Some(fdecl::StartupMode::Lazy),
1926                         on_terminate: None,
1927                         environment: None,
1928                         ..Default::default()
1929                     },
1930                     fdecl::Child {
1931                         name: Some("gtest".to_string()),
1932                         url: Some("fuchsia-pkg://fuchsia.com/gtest#meta/gtest.cm".to_string()),
1933                         startup: Some(fdecl::StartupMode::Lazy),
1934                         on_terminate: Some(fdecl::OnTerminate::None),
1935                         environment: None,
1936                         ..Default::default()
1937                     },
1938                     fdecl::Child {
1939                         name: Some("echo".to_string()),
1940                         url: Some("fuchsia-pkg://fuchsia.com/echo#meta/echo.cm"
1941                                   .to_string()),
1942                         startup: Some(fdecl::StartupMode::Eager),
1943                         on_terminate: Some(fdecl::OnTerminate::Reboot),
1944                         environment: Some("test_env".to_string()),
1945                         ..Default::default()
1946                     },
1947                ]),
1948                collections: Some(vec![
1949                     fdecl::Collection {
1950                         name: Some("modular".to_string()),
1951                         durability: Some(fdecl::Durability::Transient),
1952                         environment: None,
1953                         allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
1954                         allow_long_names: Some(true),
1955                         persistent_storage: None,
1956                         ..Default::default()
1957                     },
1958                     fdecl::Collection {
1959                         name: Some("tests".to_string()),
1960                         durability: Some(fdecl::Durability::Transient),
1961                         environment: Some("test_env".to_string()),
1962                         allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
1963                         allow_long_names: Some(true),
1964                         persistent_storage: Some(true),
1965                         ..Default::default()
1966                     },
1967                ]),
1968                facets: Some(fdata::Dictionary {
1969                    entries: Some(vec![
1970                        fdata::DictionaryEntry {
1971                            key: "author".to_string(),
1972                            value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
1973                        },
1974                    ]),
1975                    ..Default::default()
1976                }),
1977                environments: Some(vec![
1978                    fdecl::Environment {
1979                        name: Some("test_env".to_string()),
1980                        extends: Some(fdecl::EnvironmentExtends::Realm),
1981                        runners: Some(vec![
1982                            fdecl::RunnerRegistration {
1983                                source_name: Some("runner".to_string()),
1984                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1985                                    name: "gtest".to_string(),
1986                                    collection: None,
1987                                })),
1988                                target_name: Some("gtest-runner".to_string()),
1989                                ..Default::default()
1990                            }
1991                        ]),
1992                        resolvers: Some(vec![
1993                            fdecl::ResolverRegistration {
1994                                resolver: Some("pkg_resolver".to_string()),
1995                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
1996                                scheme: Some("fuchsia-pkg".to_string()),
1997                                ..Default::default()
1998                            }
1999                        ]),
2000                        debug_capabilities: Some(vec![
2001                         fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
2002                             source_name: Some("some_protocol".to_string()),
2003                             source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2004                                 name: "gtest".to_string(),
2005                                 collection: None,
2006                             })),
2007                             target_name: Some("some_protocol".to_string()),
2008                             ..Default::default()
2009                            })
2010                        ]),
2011                        stop_timeout_ms: Some(4567),
2012                        ..Default::default()
2013                    }
2014                ]),
2015                config: Some(fdecl::ConfigSchema{
2016                    fields: Some(vec![
2017                        fdecl::ConfigField {
2018                            key: Some("enable_logging".to_string()),
2019                            type_: Some(fdecl::ConfigType {
2020                                layout: fdecl::ConfigTypeLayout::Bool,
2021                                parameters: Some(vec![]),
2022                                constraints: vec![],
2023                            }),
2024                            mutability: Some(Default::default()),
2025                            ..Default::default()
2026                        }
2027                    ]),
2028                    checksum: Some(fdecl::ConfigChecksum::Sha256([
2029                        0x64, 0x49, 0x9E, 0x75, 0xF3, 0x37, 0x69, 0x88, 0x74, 0x3B, 0x38, 0x16,
2030                        0xCD, 0x14, 0x70, 0x9F, 0x3D, 0x4A, 0xD3, 0xE2, 0x24, 0x9A, 0x1A, 0x34,
2031                        0x80, 0xB4, 0x9E, 0xB9, 0x63, 0x57, 0xD6, 0xED,
2032                    ])),
2033                    value_source: Some(
2034                        fdecl::ConfigValueSource::PackagePath("fake.cvf".to_string())
2035                    ),
2036                    ..Default::default()
2037                }),
2038                ..Default::default()
2039            },
2040            result = {
2041                ComponentDecl {
2042                    program: Some(ProgramDecl {
2043                        runner: Some("elf".parse().unwrap()),
2044                        info: fdata::Dictionary {
2045                            entries: Some(vec![
2046                                fdata::DictionaryEntry {
2047                                    key: "args".to_string(),
2048                                    value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["foo".to_string(), "bar".to_string()]))),
2049                                },
2050                                fdata::DictionaryEntry{
2051                                    key: "binary".to_string(),
2052                                    value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
2053                                },
2054                            ]),
2055                            ..Default::default()
2056                        },
2057                    }),
2058                    uses: Box::from([
2059                        UseDecl::Service(UseServiceDecl {
2060                            dependency_type: DependencyType::Strong,
2061                            source: UseSource::Parent,
2062                            source_name: "netstack".parse().unwrap(),
2063                            source_dictionary: "in/dict".parse().unwrap(),
2064                            target_path: "/svc/mynetstack".parse().unwrap(),
2065                            availability: Availability::Required,
2066                        }),
2067                        UseDecl::Protocol(UseProtocolDecl {
2068                            dependency_type: DependencyType::Strong,
2069                            source: UseSource::Parent,
2070                            source_name: "legacy_netstack".parse().unwrap(),
2071                            source_dictionary: "in/dict".parse().unwrap(),
2072                            target_path: None,
2073                            numbered_handle: Some(HandleType::from(0xab)),
2074                            availability: Availability::Optional,
2075                        }),
2076                        UseDecl::Protocol(UseProtocolDecl {
2077                            dependency_type: DependencyType::Strong,
2078                            source: UseSource::Child("echo".parse().unwrap()),
2079                            source_name: "echo_service".parse().unwrap(),
2080                            source_dictionary: "in/dict".parse().unwrap(),
2081                            target_path: Some("/svc/echo_service".parse().unwrap()),
2082                            numbered_handle: None,
2083                            availability: Availability::Required,
2084                        }),
2085                        UseDecl::Directory(UseDirectoryDecl {
2086                            dependency_type: DependencyType::Strong,
2087                            source: UseSource::Self_,
2088                            source_name: "dir".parse().unwrap(),
2089                            source_dictionary: "dict1/me".parse().unwrap(),
2090                            target_path: "/data".parse().unwrap(),
2091                            rights: fio::Operations::CONNECT,
2092                            subdir: "foo/bar".parse().unwrap(),
2093                            availability: Availability::Required,
2094                        }),
2095                        UseDecl::Storage(UseStorageDecl {
2096                            source_name: "cache".parse().unwrap(),
2097                            target_path: "/cache".parse().unwrap(),
2098                            availability: Availability::Required,
2099                        }),
2100                        UseDecl::Storage(UseStorageDecl {
2101                            source_name: "temp".parse().unwrap(),
2102                            target_path: "/temp".parse().unwrap(),
2103                            availability: Availability::Optional,
2104                        }),
2105                        UseDecl::EventStream(Box::new(UseEventStreamDecl {
2106                            source: UseSource::Child("netstack".parse().unwrap()),
2107                            scope: Some(Box::from([EventScope::Child(ChildRef{ name: "a".parse().unwrap(), collection: None}), EventScope::Collection("b".parse().unwrap())])),
2108                            source_name: "stopped".parse().unwrap(),
2109                            target_path: "/svc/test".parse().unwrap(),
2110                            filter: None,
2111                            availability: Availability::Optional,
2112                        })),
2113                        UseDecl::Runner(UseRunnerDecl {
2114                            source: UseSource::Environment,
2115                            source_name: "elf".parse().unwrap(),
2116                            source_dictionary: ".".parse().unwrap(),
2117                        }),
2118                        UseDecl::Config(Box::new(UseConfigurationDecl {
2119                            source: UseSource::Parent,
2120                            source_name: "fuchsia.config.MyConfig".parse().unwrap(),
2121                            target_name: "my_config".parse().unwrap(),
2122                            availability: Availability::Required,
2123                            type_: ConfigValueType::Bool,
2124                            default: None,
2125                            source_dictionary: ".".parse().unwrap(),
2126                        })),
2127                        #[cfg(fuchsia_api_level_at_least = "29")]
2128                        UseDecl::Dictionary(UseDictionaryDecl {
2129                            dependency_type: DependencyType::Strong,
2130                            source: UseSource::Parent,
2131                            source_name: "dictionary".parse().unwrap(),
2132                            source_dictionary: "other_dictionary".parse().unwrap(),
2133                            target_path: "/svc".parse().unwrap(),
2134                            availability: Availability::Optional,
2135                        }),
2136                    ]),
2137                    exposes: Box::from([
2138                        ExposeDecl::Protocol(ExposeProtocolDecl {
2139                            source: ExposeSource::Child("netstack".parse().unwrap()),
2140                            source_name: "legacy_netstack".parse().unwrap(),
2141                            source_dictionary: "in/dict".parse().unwrap(),
2142                            target_name: "legacy_mynetstack".parse().unwrap(),
2143                            target: ExposeTarget::Parent,
2144                            availability: Availability::Required,
2145                        }),
2146                        ExposeDecl::Directory(ExposeDirectoryDecl {
2147                            source: ExposeSource::Child("netstack".parse().unwrap()),
2148                            source_name: "dir".parse().unwrap(),
2149                            source_dictionary: "in/dict".parse().unwrap(),
2150                            target_name: "data".parse().unwrap(),
2151                            target: ExposeTarget::Parent,
2152                            rights: Some(fio::Operations::CONNECT),
2153                            subdir: "foo/bar".parse().unwrap(),
2154                            availability: Availability::Optional,
2155                        }),
2156                        ExposeDecl::Runner(ExposeRunnerDecl {
2157                            source: ExposeSource::Child("netstack".parse().unwrap()),
2158                            source_name: "elf".parse().unwrap(),
2159                            source_dictionary: "in/dict".parse().unwrap(),
2160                            target: ExposeTarget::Parent,
2161                            target_name: "elf".parse().unwrap(),
2162                        }),
2163                        ExposeDecl::Resolver(ExposeResolverDecl {
2164                            source: ExposeSource::Child("netstack".parse().unwrap()),
2165                            source_name: "pkg".parse().unwrap(),
2166                            source_dictionary: "in/dict".parse().unwrap(),
2167                            target: ExposeTarget::Parent,
2168                            target_name: "pkg".parse().unwrap(),
2169                        }),
2170                        ExposeDecl::Service(ExposeServiceDecl {
2171                            source: ExposeSource::Child("netstack".parse().unwrap()),
2172                            source_name: "netstack1".parse().unwrap(),
2173                            source_dictionary: "in/dict".parse().unwrap(),
2174                            target_name: "mynetstack".parse().unwrap(),
2175                            target: ExposeTarget::Parent,
2176                            availability: Availability::Required,
2177                        }),
2178                        ExposeDecl::Service(ExposeServiceDecl {
2179                            source: ExposeSource::Collection("modular".parse().unwrap()),
2180                            source_name: "netstack2".parse().unwrap(),
2181                            source_dictionary: ".".parse().unwrap(),
2182                            target_name: "mynetstack".parse().unwrap(),
2183                            target: ExposeTarget::Parent,
2184                            availability: Availability::Required,
2185                        }),
2186                        ExposeDecl::Dictionary(ExposeDictionaryDecl {
2187                            source: ExposeSource::Child("netstack".parse().unwrap()),
2188                            source_name: "bundle".parse().unwrap(),
2189                            source_dictionary: "in/dict".parse().unwrap(),
2190                            target_name: "mybundle".parse().unwrap(),
2191                            target: ExposeTarget::Parent,
2192                            availability: Availability::Required,
2193                        }),
2194                    ]),
2195                    offers: Box::from([
2196                        OfferDecl::Protocol(OfferProtocolDecl {
2197                            source: OfferSource::Parent,
2198                            source_name: "legacy_netstack".parse().unwrap(),
2199                            source_dictionary: "in/dict".parse().unwrap(),
2200                            target: offer_target_static_child("echo"),
2201                            target_name: "legacy_mynetstack".parse().unwrap(),
2202                            dependency_type: DependencyType::Weak,
2203                            availability: Availability::Required,
2204                        }),
2205                        OfferDecl::Directory(Box::new(OfferDirectoryDecl {
2206                            source: OfferSource::Parent,
2207                            source_name: "dir".parse().unwrap(),
2208                            source_dictionary: "in/dict".parse().unwrap(),
2209                            target: OfferTarget::Collection("modular".parse().unwrap()),
2210                            target_name: "data".parse().unwrap(),
2211                            rights: Some(fio::Operations::CONNECT),
2212                            subdir: ".".parse().unwrap(),
2213                            dependency_type: DependencyType::Strong,
2214                            availability: Availability::Optional,
2215                        })),
2216                        OfferDecl::Storage(OfferStorageDecl {
2217                            source_name: "cache".parse().unwrap(),
2218                            source: OfferSource::Self_,
2219                            target: OfferTarget::Collection("modular".parse().unwrap()),
2220                            target_name: "cache".parse().unwrap(),
2221                            availability: Availability::Required,
2222                        }),
2223                        OfferDecl::Runner(OfferRunnerDecl {
2224                            source: OfferSource::Parent,
2225                            source_name: "elf".parse().unwrap(),
2226                            source_dictionary: "in/dict".parse().unwrap(),
2227                            target: offer_target_static_child("echo"),
2228                            target_name: "elf2".parse().unwrap(),
2229                        }),
2230                        OfferDecl::Resolver(OfferResolverDecl {
2231                            source: OfferSource::Parent,
2232                            source_name: "pkg".parse().unwrap(),
2233                            source_dictionary: "in/dict".parse().unwrap(),
2234                            target: offer_target_static_child("echo"),
2235                            target_name: "pkg".parse().unwrap(),
2236                        }),
2237                        OfferDecl::Service(Box::new(OfferServiceDecl {
2238                            source: OfferSource::Parent,
2239                            source_name: "netstack1".parse().unwrap(),
2240                            source_dictionary: "in/dict".parse().unwrap(),
2241                            source_instance_filter: None,
2242                            renamed_instances: None,
2243                            target: offer_target_static_child("echo"),
2244                            target_name: "mynetstack1".parse().unwrap(),
2245                            availability: Availability::Required,
2246                            dependency_type: Default::default(),
2247                        })),
2248                        OfferDecl::Service(Box::new(OfferServiceDecl {
2249                            source: OfferSource::Parent,
2250                            source_name: "netstack2".parse().unwrap(),
2251                            source_dictionary: ".".parse().unwrap(),
2252                            source_instance_filter: None,
2253                            renamed_instances: None,
2254                            target: offer_target_static_child("echo"),
2255                            target_name: "mynetstack2".parse().unwrap(),
2256                            availability: Availability::Optional,
2257                            dependency_type: Default::default(),
2258                        })),
2259                        OfferDecl::Service(Box::new(OfferServiceDecl {
2260                            source: OfferSource::Parent,
2261                            source_name: "netstack3".parse().unwrap(),
2262                            source_dictionary: ".".parse().unwrap(),
2263                            source_instance_filter: Some(Box::from(["allowedinstance".parse().unwrap()])),
2264                            renamed_instances: Some(Box::from([NameMapping{source_name: "default".parse().unwrap(), target_name: "allowedinstance".parse().unwrap()}])),
2265                            target: offer_target_static_child("echo"),
2266                            target_name: "mynetstack3".parse().unwrap(),
2267                            availability: Availability::Required,
2268                            dependency_type: Default::default(),
2269                        })),
2270                        OfferDecl::Dictionary(OfferDictionaryDecl {
2271                            source: OfferSource::Parent,
2272                            source_name: "bundle".parse().unwrap(),
2273                            source_dictionary: "in/dict".parse().unwrap(),
2274                            target: offer_target_static_child("echo"),
2275                            target_name: "mybundle".parse().unwrap(),
2276                            dependency_type: DependencyType::Weak,
2277                            availability: Availability::Required,
2278                        }),
2279                    ]),
2280                    capabilities: Box::from([
2281                        CapabilityDecl::Service(ServiceDecl {
2282                            name: "netstack".parse().unwrap(),
2283                            source_path: Some("/netstack".parse().unwrap()),
2284                        }),
2285                        CapabilityDecl::Protocol(ProtocolDecl {
2286                            name: "netstack2".parse().unwrap(),
2287                            source_path: Some("/netstack2".parse().unwrap()),
2288                            delivery: DeliveryType::Immediate,
2289                        }),
2290                        CapabilityDecl::Directory(DirectoryDecl {
2291                            name: "data".parse().unwrap(),
2292                            source_path: Some("/data".parse().unwrap()),
2293                            rights: fio::Operations::CONNECT,
2294                        }),
2295                        CapabilityDecl::Storage(StorageDecl {
2296                            name: "cache".parse().unwrap(),
2297                            backing_dir: "data".parse().unwrap(),
2298                            source: StorageDirectorySource::Parent,
2299                            subdir: "cache".parse().unwrap(),
2300                            storage_id: fdecl::StorageId::StaticInstanceId,
2301                        }),
2302                        CapabilityDecl::Runner(RunnerDecl {
2303                            name: "elf".parse().unwrap(),
2304                            source_path: Some("/elf".parse().unwrap()),
2305                        }),
2306                        CapabilityDecl::Resolver(ResolverDecl {
2307                            name: "pkg".parse().unwrap(),
2308                            source_path: Some("/pkg_resolver".parse().unwrap()),
2309                        }),
2310                        CapabilityDecl::Dictionary(DictionaryDecl {
2311                            name: "dict1".parse().unwrap(),
2312                            source_path: None,
2313                        }),
2314                        CapabilityDecl::Dictionary(DictionaryDecl {
2315                            name: "dict2".parse().unwrap(),
2316                            source_path: Some("/in/other".parse().unwrap()),
2317                        }),
2318                    ]),
2319                    children: Box::from([
2320                        ChildDecl {
2321                            name: "netstack".parse().unwrap(),
2322                            url: "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cm".parse().unwrap(),
2323                            startup: fdecl::StartupMode::Lazy,
2324                            on_terminate: None,
2325                            environment: None,
2326                            config_overrides: None,
2327                        },
2328                        ChildDecl {
2329                            name: "gtest".parse().unwrap(),
2330                            url: "fuchsia-pkg://fuchsia.com/gtest#meta/gtest.cm".parse().unwrap(),
2331                            startup: fdecl::StartupMode::Lazy,
2332                            on_terminate: Some(fdecl::OnTerminate::None),
2333                            environment: None,
2334                            config_overrides: None,
2335                        },
2336                        ChildDecl {
2337                            name: "echo".parse().unwrap(),
2338                            url: "fuchsia-pkg://fuchsia.com/echo#meta/echo.cm".parse().unwrap(),
2339                            startup: fdecl::StartupMode::Eager,
2340                            on_terminate: Some(fdecl::OnTerminate::Reboot),
2341                            environment: Some("test_env".parse().unwrap()),
2342                            config_overrides: None,
2343                        },
2344                    ]),
2345                    collections: Box::from([
2346                        CollectionDecl {
2347                            name: "modular".parse().unwrap(),
2348                            durability: fdecl::Durability::Transient,
2349                            environment: None,
2350                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2351                            allow_long_names: true,
2352                            persistent_storage: None,
2353                        },
2354                        CollectionDecl {
2355                            name: "tests".parse().unwrap(),
2356                            durability: fdecl::Durability::Transient,
2357                            environment: Some("test_env".parse().unwrap()),
2358                            allowed_offers: cm_types::AllowedOffers::StaticAndDynamic,
2359                            allow_long_names: true,
2360                            persistent_storage: Some(true),
2361                        },
2362                    ]),
2363                    facets: Some(fdata::Dictionary {
2364                        entries: Some(vec![
2365                            fdata::DictionaryEntry {
2366                                key: "author".to_string(),
2367                                value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
2368                            },
2369                        ]),
2370                        ..Default::default()
2371                    }),
2372                    environments: Box::from([
2373                        EnvironmentDecl {
2374                            name: "test_env".parse().unwrap(),
2375                            extends: fdecl::EnvironmentExtends::Realm,
2376                            runners: Box::from([
2377                                RunnerRegistration {
2378                                    source_name: "runner".parse().unwrap(),
2379                                    source: RegistrationSource::Child("gtest".to_string()),
2380                                    target_name: "gtest-runner".parse().unwrap(),
2381                                }
2382                            ]),
2383                            resolvers: Box::from([
2384                                ResolverRegistration {
2385                                    resolver: "pkg_resolver".parse().unwrap(),
2386                                    source: RegistrationSource::Parent,
2387                                    scheme: "fuchsia-pkg".to_string(),
2388                                }
2389                            ]),
2390                            debug_capabilities: Box::from([
2391                                DebugRegistration::Protocol(DebugProtocolRegistration {
2392                                    source_name: "some_protocol".parse().unwrap(),
2393                                    source: RegistrationSource::Child("gtest".to_string()),
2394                                    target_name: "some_protocol".parse().unwrap(),
2395                                })
2396                            ]),
2397                            stop_timeout_ms: Some(4567),
2398                        }
2399                    ]),
2400                    config: Some(ConfigDecl {
2401                        fields: Box::from([
2402                            ConfigField {
2403                                key: "enable_logging".into(),
2404                                type_: ConfigValueType::Bool,
2405                                mutability: ConfigMutability::default(),
2406                            }
2407                        ]),
2408                        checksum: ConfigChecksum::Sha256([
2409                            0x64, 0x49, 0x9E, 0x75, 0xF3, 0x37, 0x69, 0x88, 0x74, 0x3B, 0x38, 0x16,
2410                            0xCD, 0x14, 0x70, 0x9F, 0x3D, 0x4A, 0xD3, 0xE2, 0x24, 0x9A, 0x1A, 0x34,
2411                            0x80, 0xB4, 0x9E, 0xB9, 0x63, 0x57, 0xD6, 0xED,
2412                        ]),
2413                        value_source: ConfigValueSource::PackagePath("fake.cvf".into())
2414                    }),
2415                    debug_info: None,
2416                }
2417            },
2418        },
2419    }
2420
2421    test_fidl_into_and_from! {
2422        fidl_into_and_from_use_source => {
2423            input = vec![
2424                fdecl::Ref::Parent(fdecl::ParentRef{}),
2425                fdecl::Ref::Framework(fdecl::FrameworkRef{}),
2426                fdecl::Ref::Debug(fdecl::DebugRef{}),
2427                fdecl::Ref::Capability(fdecl::CapabilityRef {name: "capability".to_string()}),
2428                fdecl::Ref::Child(fdecl::ChildRef {
2429                    name: "foo".into(),
2430                    collection: None,
2431                }),
2432                fdecl::Ref::Environment(fdecl::EnvironmentRef{}),
2433            ],
2434            input_type = fdecl::Ref,
2435            result = vec![
2436                UseSource::Parent,
2437                UseSource::Framework,
2438                UseSource::Debug,
2439                UseSource::Capability("capability".parse().unwrap()),
2440                UseSource::Child("foo".parse().unwrap()),
2441                UseSource::Environment,
2442            ],
2443            result_type = UseSource,
2444        },
2445        fidl_into_and_from_expose_source => {
2446            input = vec![
2447                fdecl::Ref::Self_(fdecl::SelfRef {}),
2448                fdecl::Ref::Child(fdecl::ChildRef {
2449                    name: "foo".into(),
2450                    collection: None,
2451                }),
2452                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2453                fdecl::Ref::Collection(fdecl::CollectionRef { name: "foo".to_string() }),
2454            ],
2455            input_type = fdecl::Ref,
2456            result = vec![
2457                ExposeSource::Self_,
2458                ExposeSource::Child("foo".parse().unwrap()),
2459                ExposeSource::Framework,
2460                ExposeSource::Collection("foo".parse().unwrap()),
2461            ],
2462            result_type = ExposeSource,
2463        },
2464        fidl_into_and_from_offer_source => {
2465            input = vec![
2466                fdecl::Ref::Self_(fdecl::SelfRef {}),
2467                fdecl::Ref::Child(fdecl::ChildRef {
2468                    name: "foo".into(),
2469                    collection: None,
2470                }),
2471                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2472                fdecl::Ref::Capability(fdecl::CapabilityRef { name: "foo".to_string() }),
2473                fdecl::Ref::Parent(fdecl::ParentRef {}),
2474                fdecl::Ref::Collection(fdecl::CollectionRef { name: "foo".to_string() }),
2475                fdecl::Ref::VoidType(fdecl::VoidRef {}),
2476            ],
2477            input_type = fdecl::Ref,
2478            result = vec![
2479                OfferSource::Self_,
2480                offer_source_static_child("foo"),
2481                OfferSource::Framework,
2482                OfferSource::Capability("foo".parse().unwrap()),
2483                OfferSource::Parent,
2484                OfferSource::Collection("foo".parse().unwrap()),
2485                OfferSource::Void,
2486            ],
2487            result_type = OfferSource,
2488        },
2489        fidl_into_and_from_dictionary_source => {
2490            input = vec![
2491                fdecl::Ref::Self_(fdecl::SelfRef {}),
2492                fdecl::Ref::Child(fdecl::ChildRef {
2493                    name: "foo".into(),
2494                    collection: None,
2495                }),
2496                fdecl::Ref::Parent(fdecl::ParentRef {}),
2497            ],
2498            input_type = fdecl::Ref,
2499            result = vec![
2500                DictionarySource::Self_,
2501                DictionarySource::Child(ChildRef {
2502                    name: "foo".parse().unwrap(),
2503                    collection: None,
2504                }),
2505                DictionarySource::Parent,
2506            ],
2507            result_type = DictionarySource,
2508        },
2509
2510        fidl_into_and_from_capability_without_path => {
2511            input = vec![
2512                fdecl::Protocol {
2513                    name: Some("foo_protocol".to_string()),
2514                    source_path: None,
2515                    delivery: Some(fdecl::DeliveryType::Immediate),
2516                    ..Default::default()
2517                },
2518            ],
2519            input_type = fdecl::Protocol,
2520            result = vec![
2521                ProtocolDecl {
2522                    name: "foo_protocol".parse().unwrap(),
2523                    source_path: None,
2524                    delivery: DeliveryType::Immediate,
2525                }
2526            ],
2527            result_type = ProtocolDecl,
2528        },
2529        fidl_into_and_from_storage_capability => {
2530            input = vec![
2531                fdecl::Storage {
2532                    name: Some("minfs".to_string()),
2533                    backing_dir: Some("minfs".into()),
2534                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2535                        name: "foo".into(),
2536                        collection: None,
2537                    })),
2538                    subdir: None,
2539                    storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
2540                    ..Default::default()
2541                },
2542            ],
2543            input_type = fdecl::Storage,
2544            result = vec![
2545                StorageDecl {
2546                    name: "minfs".parse().unwrap(),
2547                    backing_dir: "minfs".parse().unwrap(),
2548                    source: StorageDirectorySource::Child("foo".to_string()),
2549                    subdir: ".".parse().unwrap(),
2550                    storage_id: fdecl::StorageId::StaticInstanceIdOrMoniker,
2551                },
2552            ],
2553            result_type = StorageDecl,
2554        },
2555        fidl_into_and_from_storage_capability_restricted => {
2556            input = vec![
2557                fdecl::Storage {
2558                    name: Some("minfs".to_string()),
2559                    backing_dir: Some("minfs".into()),
2560                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2561                        name: "foo".into(),
2562                        collection: None,
2563                    })),
2564                    subdir: None,
2565                    storage_id: Some(fdecl::StorageId::StaticInstanceId),
2566                    ..Default::default()
2567                },
2568            ],
2569            input_type = fdecl::Storage,
2570            result = vec![
2571                StorageDecl {
2572                    name: "minfs".parse().unwrap(),
2573                    backing_dir: "minfs".parse().unwrap(),
2574                    source: StorageDirectorySource::Child("foo".to_string()),
2575                    subdir: ".".parse().unwrap(),
2576                    storage_id: fdecl::StorageId::StaticInstanceId,
2577                },
2578            ],
2579            result_type = StorageDecl,
2580        },
2581    }
2582
2583    test_fidl_into! {
2584        all_with_omitted_defaults => {
2585            input = fdecl::Component {
2586                program: Some(fdecl::Program {
2587                    runner: Some("elf".to_string()),
2588                    info: Some(fdata::Dictionary {
2589                        entries: Some(vec![]),
2590                        ..Default::default()
2591                    }),
2592                    ..Default::default()
2593                }),
2594                uses: Some(vec![]),
2595                exposes: Some(vec![]),
2596                offers: Some(vec![]),
2597                capabilities: Some(vec![]),
2598                children: Some(vec![]),
2599                collections: Some(vec![
2600                     fdecl::Collection {
2601                         name: Some("modular".to_string()),
2602                         durability: Some(fdecl::Durability::Transient),
2603                         environment: None,
2604                         allowed_offers: None,
2605                         allow_long_names: None,
2606                         persistent_storage: None,
2607                         ..Default::default()
2608                     },
2609                     fdecl::Collection {
2610                         name: Some("tests".to_string()),
2611                         durability: Some(fdecl::Durability::Transient),
2612                         environment: Some("test_env".to_string()),
2613                         allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
2614                         allow_long_names: None,
2615                         persistent_storage: Some(false),
2616                         ..Default::default()
2617                     },
2618                     fdecl::Collection {
2619                         name: Some("dyn_offers".to_string()),
2620                         durability: Some(fdecl::Durability::Transient),
2621                         allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
2622                         allow_long_names: None,
2623                         persistent_storage: Some(true),
2624                         ..Default::default()
2625                     },
2626                     fdecl::Collection {
2627                         name: Some("long_child_names".to_string()),
2628                         durability: Some(fdecl::Durability::Transient),
2629                         allowed_offers: None,
2630                         allow_long_names: Some(true),
2631                         persistent_storage: None,
2632                         ..Default::default()
2633                     },
2634                ]),
2635                facets: Some(fdata::Dictionary{
2636                    entries: Some(vec![]),
2637                    ..Default::default()
2638                }),
2639                environments: Some(vec![]),
2640                ..Default::default()
2641            },
2642            result = {
2643                ComponentDecl {
2644                    program: Some(ProgramDecl {
2645                        runner: Some("elf".parse().unwrap()),
2646                        info: fdata::Dictionary {
2647                            entries: Some(vec![]),
2648                            ..Default::default()
2649                        },
2650                    }),
2651                    uses: Box::from([]),
2652                    exposes: Box::from([]),
2653                    offers: Box::from([]),
2654                    capabilities: Box::from([]),
2655                    children: Box::from([]),
2656                    collections: Box::from([
2657                        CollectionDecl {
2658                            name: "modular".parse().unwrap(),
2659                            durability: fdecl::Durability::Transient,
2660                            environment: None,
2661                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2662                            allow_long_names: false,
2663                            persistent_storage: None,
2664                        },
2665                        CollectionDecl {
2666                            name: "tests".parse().unwrap(),
2667                            durability: fdecl::Durability::Transient,
2668                            environment: Some("test_env".parse().unwrap()),
2669                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2670                            allow_long_names: false,
2671                            persistent_storage: Some(false),
2672                        },
2673                        CollectionDecl {
2674                            name: "dyn_offers".parse().unwrap(),
2675                            durability: fdecl::Durability::Transient,
2676                            environment: None,
2677                            allowed_offers: cm_types::AllowedOffers::StaticAndDynamic,
2678                            allow_long_names: false,
2679                            persistent_storage: Some(true),
2680                        },
2681                        CollectionDecl {
2682                            name: "long_child_names".parse().unwrap(),
2683                            durability: fdecl::Durability::Transient,
2684                            environment: None,
2685                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2686                            allow_long_names: true,
2687                            persistent_storage: None,
2688                        },
2689                    ]),
2690                    facets: Some(fdata::Dictionary{
2691                        entries: Some(vec![]),
2692                        ..Default::default()
2693                    }),
2694                    environments: Box::from([]),
2695                    config: None,
2696                    debug_info: None,
2697                }
2698            },
2699        },
2700    }
2701
2702    #[test]
2703    fn default_expose_availability() {
2704        let source = fdecl::Ref::Self_(fdecl::SelfRef {});
2705        let source_name = "source";
2706        let target = fdecl::Ref::Parent(fdecl::ParentRef {});
2707        let target_name = "target";
2708        let expose_service: ExposeServiceDecl = fdecl::ExposeService {
2709            source: Some(source.clone()),
2710            source_name: Some(source_name.into()),
2711            target: Some(target.clone()),
2712            target_name: Some(target_name.into()),
2713            availability: None,
2714            ..Default::default()
2715        }
2716        .fidl_into_native();
2717        assert_eq!(*expose_service.availability(), Availability::Required);
2718
2719        let expose_protocol: ExposeProtocolDecl = fdecl::ExposeProtocol {
2720            source: Some(source.clone()),
2721            source_name: Some(source_name.into()),
2722            target: Some(target.clone()),
2723            target_name: Some(target_name.into()),
2724            ..Default::default()
2725        }
2726        .fidl_into_native();
2727        assert_eq!(*expose_protocol.availability(), Availability::Required);
2728
2729        let expose_directory: ExposeDirectoryDecl = fdecl::ExposeDirectory {
2730            source: Some(source.clone()),
2731            source_name: Some(source_name.into()),
2732            target: Some(target.clone()),
2733            target_name: Some(target_name.into()),
2734            ..Default::default()
2735        }
2736        .fidl_into_native();
2737        assert_eq!(*expose_directory.availability(), Availability::Required);
2738
2739        let expose_runner: ExposeRunnerDecl = fdecl::ExposeRunner {
2740            source: Some(source.clone()),
2741            source_name: Some(source_name.into()),
2742            target: Some(target.clone()),
2743            target_name: Some(target_name.into()),
2744            ..Default::default()
2745        }
2746        .fidl_into_native();
2747        assert_eq!(*expose_runner.availability(), Availability::Required);
2748
2749        let expose_resolver: ExposeResolverDecl = fdecl::ExposeResolver {
2750            source: Some(source.clone()),
2751            source_name: Some(source_name.into()),
2752            target: Some(target.clone()),
2753            target_name: Some(target_name.into()),
2754            ..Default::default()
2755        }
2756        .fidl_into_native();
2757        assert_eq!(*expose_resolver.availability(), Availability::Required);
2758
2759        let expose_dictionary: ExposeDictionaryDecl = fdecl::ExposeDictionary {
2760            source: Some(source.clone()),
2761            source_name: Some(source_name.into()),
2762            target: Some(target.clone()),
2763            target_name: Some(target_name.into()),
2764            ..Default::default()
2765        }
2766        .fidl_into_native();
2767        assert_eq!(*expose_dictionary.availability(), Availability::Required);
2768    }
2769
2770    #[test]
2771    fn default_delivery_type() {
2772        let protocol: ProtocolDecl = fdecl::Protocol {
2773            name: Some("foo".to_string()),
2774            source_path: Some("/foo".to_string()),
2775            delivery: None,
2776            ..Default::default()
2777        }
2778        .fidl_into_native();
2779        assert_eq!(protocol.delivery, DeliveryType::Immediate)
2780    }
2781
2782    #[test]
2783    fn on_readable_delivery_type() {
2784        let protocol: ProtocolDecl = fdecl::Protocol {
2785            name: Some("foo".to_string()),
2786            source_path: Some("/foo".to_string()),
2787            delivery: Some(fdecl::DeliveryType::OnReadable),
2788            ..Default::default()
2789        }
2790        .fidl_into_native();
2791        assert_eq!(protocol.delivery, DeliveryType::OnReadable)
2792    }
2793
2794    #[test]
2795    fn config_value_matches_type() {
2796        let bool_true = ConfigValue::Single(ConfigSingleValue::Bool(true));
2797        let bool_false = ConfigValue::Single(ConfigSingleValue::Bool(false));
2798        let uint8_zero = ConfigValue::Single(ConfigSingleValue::Uint8(0));
2799        let vec_bool_true = ConfigValue::Vector(ConfigVectorValue::BoolVector(Box::from([true])));
2800        let vec_bool_false = ConfigValue::Vector(ConfigVectorValue::BoolVector(Box::from([false])));
2801
2802        assert!(bool_true.matches_type(&bool_false));
2803        assert!(vec_bool_true.matches_type(&vec_bool_false));
2804
2805        assert!(!bool_true.matches_type(&uint8_zero));
2806        assert!(!bool_true.matches_type(&vec_bool_true));
2807    }
2808}