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