Skip to main content

cm_rust/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use cm_rust_derive::{ExposeDeclCommon, ExposeDeclCommonAlwaysRequired, FidlDecl, ToGenericRef};
6use cm_types::{AllowedOffers, BorrowedSeparatedPath, LongName, Name, Path, RelativePath, Url};
7use directed_graph::DirectedGraph;
8use fidl_fuchsia_component_decl 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, Error)]
1102pub enum GenericRef {
1103    Parent,
1104    Self_,
1105    Child(Name),
1106    ChildInCollection(LongName, Name),
1107    Collection(Name),
1108    Framework,
1109    Capability(Name),
1110    Debug,
1111    Void,
1112    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1113    Environment,
1114}
1115
1116impl std::fmt::Display for GenericRef {
1117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1118        match self {
1119            Self::Parent => write!(f, "parent"),
1120            Self::Self_ => write!(f, "self"),
1121            Self::Child(name) => write!(f, "child {name}"),
1122            Self::ChildInCollection(name, collection) => {
1123                write!(f, "child {name} in collection {collection}")
1124            }
1125            Self::Collection(name) => write!(f, "collection {name}"),
1126            Self::Framework => write!(f, "framework"),
1127            Self::Capability(name) => write!(f, "capability {name}"),
1128            Self::Debug => write!(f, "debug"),
1129            Self::Void => write!(f, "void"),
1130            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1131            Self::Environment => write!(f, "environment"),
1132        }
1133    }
1134}
1135
1136pub trait ToGenericRef {
1137    fn to_generic(self) -> GenericRef;
1138}
1139
1140impl ToGenericRef for ChildRef {
1141    fn to_generic(self) -> GenericRef {
1142        if let Some(collection) = self.collection {
1143            GenericRef::ChildInCollection(self.name, collection)
1144        } else {
1145            GenericRef::Child(self.name.to_short_infallible())
1146        }
1147    }
1148}
1149
1150impl FidlIntoNative<GenericRef> for fdecl::Ref {
1151    fn fidl_into_native(self) -> GenericRef {
1152        match self {
1153            fdecl::Ref::Parent(_) => GenericRef::Parent,
1154            fdecl::Ref::Self_(_) => GenericRef::Self_,
1155            fdecl::Ref::Child(child_ref) => {
1156                let child_ref: ChildRef = child_ref.fidl_into_native();
1157                child_ref.to_generic()
1158            }
1159            fdecl::Ref::Collection(collection) => {
1160                GenericRef::Collection(collection.name.fidl_into_native())
1161            }
1162            fdecl::Ref::Framework(_) => GenericRef::Framework,
1163            fdecl::Ref::Capability(capability) => {
1164                GenericRef::Capability(capability.name.fidl_into_native())
1165            }
1166            fdecl::Ref::Debug(_) => GenericRef::Debug,
1167            fdecl::Ref::VoidType(_) => GenericRef::Void,
1168            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1169            fdecl::Ref::Environment(_) => GenericRef::Environment,
1170            _ => panic!("unexpected fdecl::Ref variant found"),
1171        }
1172    }
1173}
1174
1175impl NativeIntoFidl<fdecl::Ref> for GenericRef {
1176    fn native_into_fidl(self) -> fdecl::Ref {
1177        match self {
1178            GenericRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
1179            GenericRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
1180            GenericRef::Child(name) => fdecl::Ref::Child(fdecl::ChildRef {
1181                name: name.native_into_fidl(),
1182                collection: None,
1183            }),
1184            GenericRef::ChildInCollection(name, collection) => fdecl::Ref::Child(fdecl::ChildRef {
1185                name: name.native_into_fidl(),
1186                collection: Some(collection.native_into_fidl()),
1187            }),
1188            GenericRef::Collection(name) => {
1189                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.native_into_fidl() })
1190            }
1191            GenericRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
1192            GenericRef::Capability(name) => {
1193                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.native_into_fidl() })
1194            }
1195            GenericRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
1196            GenericRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
1197            #[cfg(fuchsia_api_level_at_least = "HEAD")]
1198            GenericRef::Environment => fdecl::Ref::Environment(fdecl::EnvironmentRef {}),
1199        }
1200    }
1201}
1202
1203#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1204#[derive(Debug, Clone, PartialEq, Eq, Hash, ToGenericRef)]
1205pub enum EventScope {
1206    Child(ChildRef),
1207    Collection(Name),
1208}
1209
1210impl FidlIntoNative<EventScope> for fdecl::Ref {
1211    fn fidl_into_native(self) -> EventScope {
1212        match self {
1213            fdecl::Ref::Child(c) => {
1214                if let Some(_) = c.collection {
1215                    panic!("Dynamic children scopes are not supported for EventStreams");
1216                } else {
1217                    EventScope::Child(ChildRef { name: c.name.parse().unwrap(), collection: None })
1218                }
1219            }
1220            fdecl::Ref::Collection(collection) => {
1221                // cm_fidl_validator should have already validated this
1222                EventScope::Collection(collection.name.parse().unwrap())
1223            }
1224            _ => panic!("invalid EventScope variant"),
1225        }
1226    }
1227}
1228
1229#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1230#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1231pub enum ExposeSource {
1232    Self_,
1233    Child(Name),
1234    Collection(Name),
1235    Framework,
1236    Capability(Name),
1237    Void,
1238}
1239
1240impl std::fmt::Display for ExposeSource {
1241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1242        match self {
1243            Self::Framework => write!(f, "framework"),
1244            Self::Child(c) => write!(f, "child `#{}`", c),
1245            Self::Collection(c) => write!(f, "collection `#{}`", c),
1246            Self::Self_ => write!(f, "self"),
1247            Self::Capability(c) => write!(f, "capability `{}`", c),
1248            Self::Void => write!(f, "void"),
1249        }
1250    }
1251}
1252
1253impl FidlIntoNative<ExposeSource> for fdecl::Ref {
1254    fn fidl_into_native(self) -> ExposeSource {
1255        match self {
1256            fdecl::Ref::Self_(_) => ExposeSource::Self_,
1257            // cm_fidl_validator should have already validated this
1258            fdecl::Ref::Child(c) => ExposeSource::Child(c.name.parse().unwrap()),
1259            // cm_fidl_validator should have already validated this
1260            fdecl::Ref::Collection(c) => ExposeSource::Collection(c.name.parse().unwrap()),
1261            fdecl::Ref::Framework(_) => ExposeSource::Framework,
1262            // cm_fidl_validator should have already validated this
1263            fdecl::Ref::Capability(c) => ExposeSource::Capability(c.name.parse().unwrap()),
1264            fdecl::Ref::VoidType(_) => ExposeSource::Void,
1265            _ => panic!("invalid ExposeSource variant"),
1266        }
1267    }
1268}
1269
1270#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1271#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, ToGenericRef)]
1272pub enum ExposeTarget {
1273    Parent,
1274    Framework,
1275}
1276
1277impl std::fmt::Display for ExposeTarget {
1278    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1279        match self {
1280            Self::Framework => write!(f, "framework"),
1281            Self::Parent => write!(f, "parent"),
1282        }
1283    }
1284}
1285
1286impl FidlIntoNative<ExposeTarget> for fdecl::Ref {
1287    fn fidl_into_native(self) -> ExposeTarget {
1288        match self {
1289            fdecl::Ref::Parent(_) => ExposeTarget::Parent,
1290            fdecl::Ref::Framework(_) => ExposeTarget::Framework,
1291            _ => panic!("invalid ExposeTarget variant"),
1292        }
1293    }
1294}
1295
1296/// A source for a service.
1297#[derive(Debug, Clone, PartialEq, Eq)]
1298pub struct ServiceSource<T> {
1299    /// The provider of the service, relative to a component.
1300    pub source: T,
1301    /// The name of the service.
1302    pub source_name: Name,
1303}
1304
1305#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1306#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1307pub enum StorageDirectorySource {
1308    Parent,
1309    Self_,
1310    Child(String),
1311}
1312
1313impl FidlIntoNative<StorageDirectorySource> for fdecl::Ref {
1314    fn fidl_into_native(self) -> StorageDirectorySource {
1315        match self {
1316            fdecl::Ref::Parent(_) => StorageDirectorySource::Parent,
1317            fdecl::Ref::Self_(_) => StorageDirectorySource::Self_,
1318            fdecl::Ref::Child(c) => StorageDirectorySource::Child(c.name),
1319            _ => panic!("invalid StorageDirectorySource variant"),
1320        }
1321    }
1322}
1323
1324#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1325#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1326pub enum DictionarySource {
1327    Parent,
1328    Self_,
1329    Child(ChildRef),
1330}
1331
1332impl FidlIntoNative<DictionarySource> for fdecl::Ref {
1333    fn fidl_into_native(self) -> DictionarySource {
1334        match self {
1335            Self::Parent(_) => DictionarySource::Parent,
1336            Self::Self_(_) => DictionarySource::Self_,
1337            Self::Child(c) => DictionarySource::Child(c.fidl_into_native()),
1338            _ => panic!("invalid DictionarySource variant"),
1339        }
1340    }
1341}
1342
1343#[cfg_attr(feature = "serde", derive(Deserialize, Serialize), serde(rename_all = "snake_case"))]
1344#[derive(Debug, Clone, PartialEq, Eq, ToGenericRef)]
1345pub enum RegistrationSource {
1346    Parent,
1347    Self_,
1348    Child(String),
1349}
1350
1351impl FidlIntoNative<RegistrationSource> for fdecl::Ref {
1352    fn fidl_into_native(self) -> RegistrationSource {
1353        match self {
1354            fdecl::Ref::Parent(_) => RegistrationSource::Parent,
1355            fdecl::Ref::Self_(_) => RegistrationSource::Self_,
1356            fdecl::Ref::Child(c) => RegistrationSource::Child(c.name),
1357            _ => panic!("invalid RegistrationSource variant"),
1358        }
1359    }
1360}
1361
1362/// Converts the contents of a CM-FIDL declaration and produces the equivalent CM-Rust
1363/// struct.
1364/// This function applies cm_fidl_validator to check correctness.
1365impl TryFrom<fdecl::Component> for ComponentDecl {
1366    type Error = Error;
1367
1368    fn try_from(decl: fdecl::Component) -> Result<Self, Self::Error> {
1369        cm_fidl_validator::validate(&decl, &mut DirectedGraph::new())
1370            .map_err(|err| Error::Validate { err })?;
1371        Ok(decl.fidl_into_native())
1372    }
1373}
1374
1375// Converts the contents of a CM-Rust declaration into a CM_FIDL declaration
1376impl From<ComponentDecl> for fdecl::Component {
1377    fn from(decl: ComponentDecl) -> Self {
1378        decl.native_into_fidl()
1379    }
1380}
1381
1382/// Errors produced by cm_rust.
1383#[derive(Debug, Error, Clone)]
1384pub enum Error {
1385    #[error("Fidl validation failed: {}", err)]
1386    Validate {
1387        #[source]
1388        err: cm_fidl_validator::error::ErrorList,
1389    },
1390    #[error("Invalid capability path: {}", raw)]
1391    InvalidCapabilityPath { raw: String },
1392    #[error("Invalid capability type name: {}", raw)]
1393    ParseCapabilityTypeName { raw: String },
1394}
1395
1396/// Push `value` onto the end of `Box<[T]>`. Convenience function for clients that work with
1397/// cm_rust, which uses `Box<[T]>` instead of `Vec<T>` for its list type.
1398pub fn push_box<T>(container: &mut Box<[T]>, value: T) {
1399    let boxed = mem::replace(container, Box::from([]));
1400    let mut new_container: Vec<_> = boxed.into();
1401    new_container.push(value);
1402    *container = new_container.into();
1403}
1404
1405/// Append `other` to the end of `Box<[T]>`. Convenience function for clients that work with
1406/// cm_rust, which uses `Box<[T]>` instead of `Vec<T>` for its list type.
1407pub fn append_box<T>(container: &mut Box<[T]>, other: &mut Vec<T>) {
1408    let boxed = mem::replace(container, Box::from([]));
1409    let mut new_container: Vec<_> = boxed.into();
1410    new_container.append(other);
1411    *container = new_container.into();
1412}
1413
1414#[cfg(test)]
1415mod tests {
1416    use super::*;
1417    use difference::Changeset;
1418    use fidl_fuchsia_component_decl as fdecl;
1419
1420    fn offer_source_static_child(name: &str) -> OfferSource {
1421        OfferSource::Child(ChildRef { name: name.parse().unwrap(), collection: None })
1422    }
1423
1424    fn offer_target_static_child(name: &str) -> OfferTarget {
1425        OfferTarget::Child(ChildRef { name: name.parse().unwrap(), collection: None })
1426    }
1427
1428    macro_rules! test_try_from_decl {
1429        (
1430            $(
1431                $test_name:ident => {
1432                    input = $input:expr,
1433                    result = $result:expr,
1434                },
1435            )+
1436        ) => {
1437            $(
1438                #[test]
1439                fn $test_name() {
1440                    {
1441                        let res = ComponentDecl::try_from($input).expect("try_from failed");
1442                        if res != $result {
1443                            let a = format!("{:#?}", res);
1444                            let e = format!("{:#?}", $result);
1445                            panic!("Conversion from fidl to cm_rust did not yield expected result:\n{}", Changeset::new(&a, &e, "\n"));
1446                        }
1447                    }
1448                    {
1449                        let res = fdecl::Component::try_from($result).expect("try_from failed");
1450                        if res != $input {
1451                            let a = format!("{:#?}", res);
1452                            let e = format!("{:#?}", $input);
1453                            panic!("Conversion from cm_rust to fidl did not yield expected result:\n{}", Changeset::new(&a, &e, "\n"));
1454                        }
1455                    }
1456                }
1457            )+
1458        }
1459    }
1460
1461    macro_rules! test_fidl_into_and_from {
1462        (
1463            $(
1464                $test_name:ident => {
1465                    input = $input:expr,
1466                    input_type = $input_type:ty,
1467                    result = $result:expr,
1468                    result_type = $result_type:ty,
1469                },
1470            )+
1471        ) => {
1472            $(
1473                #[test]
1474                fn $test_name() {
1475                    {
1476                        let res: Vec<$result_type> =
1477                            $input.into_iter().map(|e| e.fidl_into_native()).collect();
1478                        assert_eq!(res, $result);
1479                    }
1480                    {
1481                        let res: Vec<$input_type> =
1482                            $result.into_iter().map(|e| e.native_into_fidl()).collect();
1483                        assert_eq!(res, $input);
1484                    }
1485                }
1486            )+
1487        }
1488    }
1489
1490    macro_rules! test_fidl_into {
1491        (
1492            $(
1493                $test_name:ident => {
1494                    input = $input:expr,
1495                    result = $result:expr,
1496                },
1497            )+
1498        ) => {
1499            $(
1500                #[test]
1501                fn $test_name() {
1502                    test_fidl_into_helper($input, $result);
1503                }
1504            )+
1505        }
1506    }
1507
1508    fn test_fidl_into_helper<T, U>(input: T, expected_res: U)
1509    where
1510        T: FidlIntoNative<U>,
1511        U: std::cmp::PartialEq + std::fmt::Debug,
1512    {
1513        let res: U = input.fidl_into_native();
1514        assert_eq!(res, expected_res);
1515    }
1516
1517    test_try_from_decl! {
1518        try_from_empty => {
1519            input = fdecl::Component {
1520                program: None,
1521                uses: None,
1522                exposes: None,
1523                offers: None,
1524                capabilities: None,
1525                children: None,
1526                collections: None,
1527                facets: None,
1528                environments: None,
1529                ..Default::default()
1530            },
1531            result = ComponentDecl {
1532                program: None,
1533                uses: Box::from([]),
1534                exposes: Box::from([]),
1535                offers: Box::from([]),
1536                capabilities: Box::from([]),
1537                children: Box::from([]),
1538                collections: Box::from([]),
1539                facets: None,
1540                environments: Box::from([]),
1541                config: None,
1542                debug_info: None,
1543            },
1544        },
1545        try_from_all => {
1546            input = fdecl::Component {
1547                program: Some(fdecl::Program {
1548                    runner: Some("elf".to_string()),
1549                    info: Some(fdata::Dictionary {
1550                        entries: Some(vec![
1551                            fdata::DictionaryEntry {
1552                                key: "args".to_string(),
1553                                value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["foo".to_string(), "bar".to_string()]))),
1554                            },
1555                            fdata::DictionaryEntry {
1556                                key: "binary".to_string(),
1557                                value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
1558                            },
1559                        ]),
1560                        ..Default::default()
1561                    }),
1562                    ..Default::default()
1563                }),
1564                uses: Some(vec![
1565                    fdecl::Use::Service(fdecl::UseService {
1566                        dependency_type: Some(fdecl::DependencyType::Strong),
1567                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1568                        source_name: Some("netstack".to_string()),
1569                        source_dictionary: Some("in/dict".to_string()),
1570                        target_path: Some("/svc/mynetstack".to_string()),
1571                        availability: Some(fdecl::Availability::Required),
1572                        ..Default::default()
1573                    }),
1574                    fdecl::Use::Protocol(fdecl::UseProtocol {
1575                        dependency_type: Some(fdecl::DependencyType::Strong),
1576                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1577                        source_name: Some("legacy_netstack".to_string()),
1578                        source_dictionary: Some("in/dict".to_string()),
1579                        target_path: None,
1580                        numbered_handle: Some(0xab),
1581                        availability: Some(fdecl::Availability::Optional),
1582                        ..Default::default()
1583                    }),
1584                    fdecl::Use::Protocol(fdecl::UseProtocol {
1585                        dependency_type: Some(fdecl::DependencyType::Strong),
1586                        source: Some(fdecl::Ref::Child(fdecl::ChildRef { name: "echo".to_string(), collection: None})),
1587                        source_name: Some("echo_service".to_string()),
1588                        source_dictionary: Some("in/dict".to_string()),
1589                        target_path: Some("/svc/echo_service".to_string()),
1590                        availability: Some(fdecl::Availability::Required),
1591                        ..Default::default()
1592                    }),
1593                    fdecl::Use::Directory(fdecl::UseDirectory {
1594                        dependency_type: Some(fdecl::DependencyType::Strong),
1595                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
1596                        source_name: Some("dir".to_string()),
1597                        source_dictionary: Some("dict1/me".to_string()),
1598                        target_path: Some("/data".to_string()),
1599                        rights: Some(fio::Operations::CONNECT),
1600                        subdir: Some("foo/bar".to_string()),
1601                        availability: Some(fdecl::Availability::Required),
1602                        ..Default::default()
1603                    }),
1604                    fdecl::Use::Storage(fdecl::UseStorage {
1605                        source_name: Some("cache".to_string()),
1606                        target_path: Some("/cache".to_string()),
1607                        availability: Some(fdecl::Availability::Required),
1608                        ..Default::default()
1609                    }),
1610                    fdecl::Use::Storage(fdecl::UseStorage {
1611                        source_name: Some("temp".to_string()),
1612                        target_path: Some("/temp".to_string()),
1613                        availability: Some(fdecl::Availability::Optional),
1614                        ..Default::default()
1615                    }),
1616                    fdecl::Use::EventStream(fdecl::UseEventStream {
1617                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1618                            collection: None,
1619                            name: "netstack".to_string(),
1620                        })),
1621                        source_name: Some("stopped".to_string()),
1622                        scope: Some(vec![
1623                            fdecl::Ref::Child(fdecl::ChildRef {
1624                                collection: None,
1625                                name:"a".to_string(),
1626                        }), fdecl::Ref::Collection(fdecl::CollectionRef {
1627                            name:"b".to_string(),
1628                        })]),
1629                        target_path: Some("/svc/test".to_string()),
1630                        availability: Some(fdecl::Availability::Optional),
1631                        ..Default::default()
1632                    }),
1633                    fdecl::Use::Runner(fdecl::UseRunner {
1634                        source: Some(fdecl::Ref::Environment(fdecl::EnvironmentRef {})),
1635                        source_name: Some("elf".to_string()),
1636                        source_dictionary: None,
1637                        ..Default::default()
1638                    }),
1639                    fdecl::Use::Config(fdecl::UseConfiguration {
1640                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef)),
1641                        source_name: Some("fuchsia.config.MyConfig".to_string()),
1642                        target_name: Some("my_config".to_string()),
1643                        availability: Some(fdecl::Availability::Required),
1644                        type_: Some(fdecl::ConfigType{
1645                            layout: fdecl::ConfigTypeLayout::Bool,
1646                            parameters: Some(Vec::new()),
1647                            constraints: Vec::new(),
1648                        }),
1649                        ..Default::default()
1650                    }),
1651                    #[cfg(fuchsia_api_level_at_least = "29")]
1652                    fdecl::Use::Dictionary(fdecl::UseDictionary {
1653                        dependency_type: Some(fdecl::DependencyType::Strong),
1654                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1655                        source_name: Some("dictionary".to_string()),
1656                        source_dictionary: Some("other_dictionary".to_string()),
1657                        target_path: Some("/svc".to_string()),
1658                        availability: Some(fdecl::Availability::Optional),
1659                        ..Default::default()
1660                    }),
1661                ]),
1662                exposes: Some(vec![
1663                    fdecl::Expose::Protocol(fdecl::ExposeProtocol {
1664                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1665                            name: "netstack".to_string(),
1666                            collection: None,
1667                        })),
1668                        source_name: Some("legacy_netstack".to_string()),
1669                        source_dictionary: Some("in/dict".to_string()),
1670                        target_name: Some("legacy_mynetstack".to_string()),
1671                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1672                        availability: Some(fdecl::Availability::Required),
1673                        ..Default::default()
1674                    }),
1675                    fdecl::Expose::Directory(fdecl::ExposeDirectory {
1676                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1677                            name: "netstack".to_string(),
1678                            collection: None,
1679                        })),
1680                        source_name: Some("dir".to_string()),
1681                        source_dictionary: Some("in/dict".to_string()),
1682                        target_name: Some("data".to_string()),
1683                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1684                        rights: Some(fio::Operations::CONNECT),
1685                        subdir: Some("foo/bar".to_string()),
1686                        availability: Some(fdecl::Availability::Optional),
1687                        ..Default::default()
1688                    }),
1689                    fdecl::Expose::Runner(fdecl::ExposeRunner {
1690                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1691                            name: "netstack".to_string(),
1692                            collection: None,
1693                        })),
1694                        source_name: Some("elf".to_string()),
1695                        source_dictionary: Some("in/dict".to_string()),
1696                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1697                        target_name: Some("elf".to_string()),
1698                        ..Default::default()
1699                    }),
1700                    fdecl::Expose::Resolver(fdecl::ExposeResolver{
1701                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1702                            name: "netstack".to_string(),
1703                            collection: None,
1704                        })),
1705                        source_name: Some("pkg".to_string()),
1706                        source_dictionary: Some("in/dict".to_string()),
1707                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
1708                        target_name: Some("pkg".to_string()),
1709                        ..Default::default()
1710                    }),
1711                    fdecl::Expose::Service(fdecl::ExposeService {
1712                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1713                            name: "netstack".to_string(),
1714                            collection: None,
1715                        })),
1716                        source_name: Some("netstack1".to_string()),
1717                        source_dictionary: Some("in/dict".to_string()),
1718                        target_name: Some("mynetstack".to_string()),
1719                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1720                        availability: Some(fdecl::Availability::Required),
1721                        ..Default::default()
1722                    }),
1723                    fdecl::Expose::Service(fdecl::ExposeService {
1724                        source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
1725                            name: "modular".to_string(),
1726                        })),
1727                        source_name: Some("netstack2".to_string()),
1728                        source_dictionary: None,
1729                        target_name: Some("mynetstack".to_string()),
1730                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1731                        availability: Some(fdecl::Availability::Required),
1732                        ..Default::default()
1733                    }),
1734                    fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
1735                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1736                            name: "netstack".to_string(),
1737                            collection: None,
1738                        })),
1739                        source_name: Some("bundle".to_string()),
1740                        source_dictionary: Some("in/dict".to_string()),
1741                        target_name: Some("mybundle".to_string()),
1742                        target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1743                        availability: Some(fdecl::Availability::Required),
1744                        ..Default::default()
1745                    }),
1746                ]),
1747                offers: Some(vec![
1748                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
1749                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1750                        source_name: Some("legacy_netstack".to_string()),
1751                        source_dictionary: Some("in/dict".to_string()),
1752                        target: Some(fdecl::Ref::Child(
1753                           fdecl::ChildRef {
1754                               name: "echo".to_string(),
1755                               collection: None,
1756                           }
1757                        )),
1758                        target_name: Some("legacy_mynetstack".to_string()),
1759                        dependency_type: Some(fdecl::DependencyType::Weak),
1760                        availability: Some(fdecl::Availability::Required),
1761                        ..Default::default()
1762                    }),
1763                    fdecl::Offer::Directory(fdecl::OfferDirectory {
1764                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1765                        source_name: Some("dir".to_string()),
1766                        source_dictionary: Some("in/dict".to_string()),
1767                        target: Some(fdecl::Ref::Collection(
1768                            fdecl::CollectionRef { name: "modular".to_string() }
1769                        )),
1770                        target_name: Some("data".to_string()),
1771                        rights: Some(fio::Operations::CONNECT),
1772                        subdir: None,
1773                        dependency_type: Some(fdecl::DependencyType::Strong),
1774                        availability: Some(fdecl::Availability::Optional),
1775                        ..Default::default()
1776                    }),
1777                    fdecl::Offer::Storage(fdecl::OfferStorage {
1778                        source_name: Some("cache".to_string()),
1779                        source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
1780                        target: Some(fdecl::Ref::Collection(
1781                            fdecl::CollectionRef { name: "modular".to_string() }
1782                        )),
1783                        target_name: Some("cache".to_string()),
1784                        availability: Some(fdecl::Availability::Required),
1785                        ..Default::default()
1786                    }),
1787                    fdecl::Offer::Runner(fdecl::OfferRunner {
1788                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1789                        source_name: Some("elf".to_string()),
1790                        source_dictionary: Some("in/dict".to_string()),
1791                        target: Some(fdecl::Ref::Child(
1792                           fdecl::ChildRef {
1793                               name: "echo".to_string(),
1794                               collection: None,
1795                           }
1796                        )),
1797                        target_name: Some("elf2".to_string()),
1798                        ..Default::default()
1799                    }),
1800                    fdecl::Offer::Resolver(fdecl::OfferResolver{
1801                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
1802                        source_name: Some("pkg".to_string()),
1803                        source_dictionary: Some("in/dict".to_string()),
1804                        target: Some(fdecl::Ref::Child(
1805                           fdecl::ChildRef {
1806                              name: "echo".to_string(),
1807                              collection: None,
1808                           }
1809                        )),
1810                        target_name: Some("pkg".to_string()),
1811                        ..Default::default()
1812                    }),
1813                    fdecl::Offer::Service(fdecl::OfferService {
1814                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1815                        source_name: Some("netstack1".to_string()),
1816                        source_dictionary: Some("in/dict".to_string()),
1817                        target: Some(fdecl::Ref::Child(
1818                           fdecl::ChildRef {
1819                               name: "echo".to_string(),
1820                               collection: None,
1821                           }
1822                        )),
1823                        target_name: Some("mynetstack1".to_string()),
1824                        availability: Some(fdecl::Availability::Required),
1825                        dependency_type: Some(fdecl::DependencyType::Strong),
1826                        ..Default::default()
1827                    }),
1828                    fdecl::Offer::Service(fdecl::OfferService {
1829                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1830                        source_name: Some("netstack2".to_string()),
1831                        source_dictionary: None,
1832                        target: Some(fdecl::Ref::Child(
1833                           fdecl::ChildRef {
1834                               name: "echo".to_string(),
1835                               collection: None,
1836                           }
1837                        )),
1838                        target_name: Some("mynetstack2".to_string()),
1839                        availability: Some(fdecl::Availability::Optional),
1840                        dependency_type: Some(fdecl::DependencyType::Strong),
1841                        ..Default::default()
1842                    }),
1843                    fdecl::Offer::Service(fdecl::OfferService {
1844                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1845                        source_name: Some("netstack3".to_string()),
1846                        source_dictionary: None,
1847                        target: Some(fdecl::Ref::Child(
1848                           fdecl::ChildRef {
1849                               name: "echo".to_string(),
1850                               collection: None,
1851                           }
1852                        )),
1853                        target_name: Some("mynetstack3".to_string()),
1854                        source_instance_filter: Some(vec!["allowedinstance".to_string()]),
1855                        renamed_instances: Some(vec![fdecl::NameMapping{source_name: "default".to_string(), target_name: "allowedinstance".to_string()}]),
1856                        availability: Some(fdecl::Availability::Required),
1857                        dependency_type: Some(fdecl::DependencyType::Strong),
1858                        ..Default::default()
1859                    }),
1860                    fdecl::Offer::Dictionary(fdecl::OfferDictionary {
1861                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1862                        source_name: Some("bundle".to_string()),
1863                        source_dictionary: Some("in/dict".to_string()),
1864                        target: Some(fdecl::Ref::Child(
1865                           fdecl::ChildRef {
1866                               name: "echo".to_string(),
1867                               collection: None,
1868                           }
1869                        )),
1870                        target_name: Some("mybundle".to_string()),
1871                        dependency_type: Some(fdecl::DependencyType::Weak),
1872                        availability: Some(fdecl::Availability::Required),
1873                        ..Default::default()
1874                    }),
1875                ]),
1876                capabilities: Some(vec![
1877                    fdecl::Capability::Service(fdecl::Service {
1878                        name: Some("netstack".to_string()),
1879                        source_path: Some("/netstack".to_string()),
1880                        ..Default::default()
1881                    }),
1882                    fdecl::Capability::Protocol(fdecl::Protocol {
1883                        name: Some("netstack2".to_string()),
1884                        source_path: Some("/netstack2".to_string()),
1885                        delivery: Some(fdecl::DeliveryType::Immediate),
1886                        ..Default::default()
1887                    }),
1888                    fdecl::Capability::Directory(fdecl::Directory {
1889                        name: Some("data".to_string()),
1890                        source_path: Some("/data".to_string()),
1891                        rights: Some(fio::Operations::CONNECT),
1892                        ..Default::default()
1893                    }),
1894                    fdecl::Capability::Storage(fdecl::Storage {
1895                        name: Some("cache".to_string()),
1896                        backing_dir: Some("data".to_string()),
1897                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
1898                        subdir: Some("cache".to_string()),
1899                        storage_id: Some(fdecl::StorageId::StaticInstanceId),
1900                        ..Default::default()
1901                    }),
1902                    fdecl::Capability::Runner(fdecl::Runner {
1903                        name: Some("elf".to_string()),
1904                        source_path: Some("/elf".to_string()),
1905                        ..Default::default()
1906                    }),
1907                    fdecl::Capability::Resolver(fdecl::Resolver {
1908                        name: Some("pkg".to_string()),
1909                        source_path: Some("/pkg_resolver".to_string()),
1910                        ..Default::default()
1911                    }),
1912                    fdecl::Capability::Dictionary(fdecl::Dictionary {
1913                        name: Some("dict1".to_string()),
1914                        ..Default::default()
1915                    }),
1916                    fdecl::Capability::Dictionary(fdecl::Dictionary {
1917                        name: Some("dict2".to_string()),
1918                        source_path: Some("/in/other".to_string()),
1919                        ..Default::default()
1920                    }),
1921                ]),
1922                children: Some(vec![
1923                     fdecl::Child {
1924                         name: Some("netstack".to_string()),
1925                         url: Some("fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cm"
1926                                   .to_string()),
1927                         startup: Some(fdecl::StartupMode::Lazy),
1928                         on_terminate: None,
1929                         environment: None,
1930                         ..Default::default()
1931                     },
1932                     fdecl::Child {
1933                         name: Some("gtest".to_string()),
1934                         url: Some("fuchsia-pkg://fuchsia.com/gtest#meta/gtest.cm".to_string()),
1935                         startup: Some(fdecl::StartupMode::Lazy),
1936                         on_terminate: Some(fdecl::OnTerminate::None),
1937                         environment: None,
1938                         ..Default::default()
1939                     },
1940                     fdecl::Child {
1941                         name: Some("echo".to_string()),
1942                         url: Some("fuchsia-pkg://fuchsia.com/echo#meta/echo.cm"
1943                                   .to_string()),
1944                         startup: Some(fdecl::StartupMode::Eager),
1945                         on_terminate: Some(fdecl::OnTerminate::Reboot),
1946                         environment: Some("test_env".to_string()),
1947                         ..Default::default()
1948                     },
1949                ]),
1950                collections: Some(vec![
1951                     fdecl::Collection {
1952                         name: Some("modular".to_string()),
1953                         durability: Some(fdecl::Durability::Transient),
1954                         environment: None,
1955                         allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
1956                         allow_long_names: Some(true),
1957                         persistent_storage: None,
1958                         ..Default::default()
1959                     },
1960                     fdecl::Collection {
1961                         name: Some("tests".to_string()),
1962                         durability: Some(fdecl::Durability::Transient),
1963                         environment: Some("test_env".to_string()),
1964                         allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
1965                         allow_long_names: Some(true),
1966                         persistent_storage: Some(true),
1967                         ..Default::default()
1968                     },
1969                ]),
1970                facets: Some(fdata::Dictionary {
1971                    entries: Some(vec![
1972                        fdata::DictionaryEntry {
1973                            key: "author".to_string(),
1974                            value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
1975                        },
1976                    ]),
1977                    ..Default::default()
1978                }),
1979                environments: Some(vec![
1980                    fdecl::Environment {
1981                        name: Some("test_env".to_string()),
1982                        extends: Some(fdecl::EnvironmentExtends::Realm),
1983                        runners: Some(vec![
1984                            fdecl::RunnerRegistration {
1985                                source_name: Some("runner".to_string()),
1986                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
1987                                    name: "gtest".to_string(),
1988                                    collection: None,
1989                                })),
1990                                target_name: Some("gtest-runner".to_string()),
1991                                ..Default::default()
1992                            }
1993                        ]),
1994                        resolvers: Some(vec![
1995                            fdecl::ResolverRegistration {
1996                                resolver: Some("pkg_resolver".to_string()),
1997                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
1998                                scheme: Some("fuchsia-pkg".to_string()),
1999                                ..Default::default()
2000                            }
2001                        ]),
2002                        debug_capabilities: Some(vec![
2003                         fdecl::DebugRegistration::Protocol(fdecl::DebugProtocolRegistration {
2004                             source_name: Some("some_protocol".to_string()),
2005                             source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2006                                 name: "gtest".to_string(),
2007                                 collection: None,
2008                             })),
2009                             target_name: Some("some_protocol".to_string()),
2010                             ..Default::default()
2011                            })
2012                        ]),
2013                        stop_timeout_ms: Some(4567),
2014                        ..Default::default()
2015                    }
2016                ]),
2017                config: Some(fdecl::ConfigSchema{
2018                    fields: Some(vec![
2019                        fdecl::ConfigField {
2020                            key: Some("enable_logging".to_string()),
2021                            type_: Some(fdecl::ConfigType {
2022                                layout: fdecl::ConfigTypeLayout::Bool,
2023                                parameters: Some(vec![]),
2024                                constraints: vec![],
2025                            }),
2026                            mutability: Some(Default::default()),
2027                            ..Default::default()
2028                        }
2029                    ]),
2030                    checksum: Some(fdecl::ConfigChecksum::Sha256([
2031                        0x64, 0x49, 0x9E, 0x75, 0xF3, 0x37, 0x69, 0x88, 0x74, 0x3B, 0x38, 0x16,
2032                        0xCD, 0x14, 0x70, 0x9F, 0x3D, 0x4A, 0xD3, 0xE2, 0x24, 0x9A, 0x1A, 0x34,
2033                        0x80, 0xB4, 0x9E, 0xB9, 0x63, 0x57, 0xD6, 0xED,
2034                    ])),
2035                    value_source: Some(
2036                        fdecl::ConfigValueSource::PackagePath("fake.cvf".to_string())
2037                    ),
2038                    ..Default::default()
2039                }),
2040                ..Default::default()
2041            },
2042            result = {
2043                ComponentDecl {
2044                    program: Some(ProgramDecl {
2045                        runner: Some("elf".parse().unwrap()),
2046                        info: fdata::Dictionary {
2047                            entries: Some(vec![
2048                                fdata::DictionaryEntry {
2049                                    key: "args".to_string(),
2050                                    value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["foo".to_string(), "bar".to_string()]))),
2051                                },
2052                                fdata::DictionaryEntry{
2053                                    key: "binary".to_string(),
2054                                    value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
2055                                },
2056                            ]),
2057                            ..Default::default()
2058                        },
2059                    }),
2060                    uses: Box::from([
2061                        UseDecl::Service(UseServiceDecl {
2062                            dependency_type: DependencyType::Strong,
2063                            source: UseSource::Parent,
2064                            source_name: "netstack".parse().unwrap(),
2065                            source_dictionary: "in/dict".parse().unwrap(),
2066                            target_path: "/svc/mynetstack".parse().unwrap(),
2067                            availability: Availability::Required,
2068                        }),
2069                        UseDecl::Protocol(UseProtocolDecl {
2070                            dependency_type: DependencyType::Strong,
2071                            source: UseSource::Parent,
2072                            source_name: "legacy_netstack".parse().unwrap(),
2073                            source_dictionary: "in/dict".parse().unwrap(),
2074                            target_path: None,
2075                            numbered_handle: Some(HandleType::from(0xab)),
2076                            availability: Availability::Optional,
2077                        }),
2078                        UseDecl::Protocol(UseProtocolDecl {
2079                            dependency_type: DependencyType::Strong,
2080                            source: UseSource::Child("echo".parse().unwrap()),
2081                            source_name: "echo_service".parse().unwrap(),
2082                            source_dictionary: "in/dict".parse().unwrap(),
2083                            target_path: Some("/svc/echo_service".parse().unwrap()),
2084                            numbered_handle: None,
2085                            availability: Availability::Required,
2086                        }),
2087                        UseDecl::Directory(UseDirectoryDecl {
2088                            dependency_type: DependencyType::Strong,
2089                            source: UseSource::Self_,
2090                            source_name: "dir".parse().unwrap(),
2091                            source_dictionary: "dict1/me".parse().unwrap(),
2092                            target_path: "/data".parse().unwrap(),
2093                            rights: fio::Operations::CONNECT,
2094                            subdir: "foo/bar".parse().unwrap(),
2095                            availability: Availability::Required,
2096                        }),
2097                        UseDecl::Storage(UseStorageDecl {
2098                            source_name: "cache".parse().unwrap(),
2099                            target_path: "/cache".parse().unwrap(),
2100                            availability: Availability::Required,
2101                        }),
2102                        UseDecl::Storage(UseStorageDecl {
2103                            source_name: "temp".parse().unwrap(),
2104                            target_path: "/temp".parse().unwrap(),
2105                            availability: Availability::Optional,
2106                        }),
2107                        UseDecl::EventStream(Box::new(UseEventStreamDecl {
2108                            source: UseSource::Child("netstack".parse().unwrap()),
2109                            scope: Some(Box::from([EventScope::Child(ChildRef{ name: "a".parse().unwrap(), collection: None}), EventScope::Collection("b".parse().unwrap())])),
2110                            source_name: "stopped".parse().unwrap(),
2111                            target_path: "/svc/test".parse().unwrap(),
2112                            filter: None,
2113                            availability: Availability::Optional,
2114                        })),
2115                        UseDecl::Runner(UseRunnerDecl {
2116                            source: UseSource::Environment,
2117                            source_name: "elf".parse().unwrap(),
2118                            source_dictionary: ".".parse().unwrap(),
2119                        }),
2120                        UseDecl::Config(Box::new(UseConfigurationDecl {
2121                            source: UseSource::Parent,
2122                            source_name: "fuchsia.config.MyConfig".parse().unwrap(),
2123                            target_name: "my_config".parse().unwrap(),
2124                            availability: Availability::Required,
2125                            type_: ConfigValueType::Bool,
2126                            default: None,
2127                            source_dictionary: ".".parse().unwrap(),
2128                        })),
2129                        #[cfg(fuchsia_api_level_at_least = "29")]
2130                        UseDecl::Dictionary(UseDictionaryDecl {
2131                            dependency_type: DependencyType::Strong,
2132                            source: UseSource::Parent,
2133                            source_name: "dictionary".parse().unwrap(),
2134                            source_dictionary: "other_dictionary".parse().unwrap(),
2135                            target_path: "/svc".parse().unwrap(),
2136                            availability: Availability::Optional,
2137                        }),
2138                    ]),
2139                    exposes: Box::from([
2140                        ExposeDecl::Protocol(ExposeProtocolDecl {
2141                            source: ExposeSource::Child("netstack".parse().unwrap()),
2142                            source_name: "legacy_netstack".parse().unwrap(),
2143                            source_dictionary: "in/dict".parse().unwrap(),
2144                            target_name: "legacy_mynetstack".parse().unwrap(),
2145                            target: ExposeTarget::Parent,
2146                            availability: Availability::Required,
2147                        }),
2148                        ExposeDecl::Directory(ExposeDirectoryDecl {
2149                            source: ExposeSource::Child("netstack".parse().unwrap()),
2150                            source_name: "dir".parse().unwrap(),
2151                            source_dictionary: "in/dict".parse().unwrap(),
2152                            target_name: "data".parse().unwrap(),
2153                            target: ExposeTarget::Parent,
2154                            rights: Some(fio::Operations::CONNECT),
2155                            subdir: "foo/bar".parse().unwrap(),
2156                            availability: Availability::Optional,
2157                        }),
2158                        ExposeDecl::Runner(ExposeRunnerDecl {
2159                            source: ExposeSource::Child("netstack".parse().unwrap()),
2160                            source_name: "elf".parse().unwrap(),
2161                            source_dictionary: "in/dict".parse().unwrap(),
2162                            target: ExposeTarget::Parent,
2163                            target_name: "elf".parse().unwrap(),
2164                        }),
2165                        ExposeDecl::Resolver(ExposeResolverDecl {
2166                            source: ExposeSource::Child("netstack".parse().unwrap()),
2167                            source_name: "pkg".parse().unwrap(),
2168                            source_dictionary: "in/dict".parse().unwrap(),
2169                            target: ExposeTarget::Parent,
2170                            target_name: "pkg".parse().unwrap(),
2171                        }),
2172                        ExposeDecl::Service(ExposeServiceDecl {
2173                            source: ExposeSource::Child("netstack".parse().unwrap()),
2174                            source_name: "netstack1".parse().unwrap(),
2175                            source_dictionary: "in/dict".parse().unwrap(),
2176                            target_name: "mynetstack".parse().unwrap(),
2177                            target: ExposeTarget::Parent,
2178                            availability: Availability::Required,
2179                        }),
2180                        ExposeDecl::Service(ExposeServiceDecl {
2181                            source: ExposeSource::Collection("modular".parse().unwrap()),
2182                            source_name: "netstack2".parse().unwrap(),
2183                            source_dictionary: ".".parse().unwrap(),
2184                            target_name: "mynetstack".parse().unwrap(),
2185                            target: ExposeTarget::Parent,
2186                            availability: Availability::Required,
2187                        }),
2188                        ExposeDecl::Dictionary(ExposeDictionaryDecl {
2189                            source: ExposeSource::Child("netstack".parse().unwrap()),
2190                            source_name: "bundle".parse().unwrap(),
2191                            source_dictionary: "in/dict".parse().unwrap(),
2192                            target_name: "mybundle".parse().unwrap(),
2193                            target: ExposeTarget::Parent,
2194                            availability: Availability::Required,
2195                        }),
2196                    ]),
2197                    offers: Box::from([
2198                        OfferDecl::Protocol(OfferProtocolDecl {
2199                            source: OfferSource::Parent,
2200                            source_name: "legacy_netstack".parse().unwrap(),
2201                            source_dictionary: "in/dict".parse().unwrap(),
2202                            target: offer_target_static_child("echo"),
2203                            target_name: "legacy_mynetstack".parse().unwrap(),
2204                            dependency_type: DependencyType::Weak,
2205                            availability: Availability::Required,
2206                        }),
2207                        OfferDecl::Directory(Box::new(OfferDirectoryDecl {
2208                            source: OfferSource::Parent,
2209                            source_name: "dir".parse().unwrap(),
2210                            source_dictionary: "in/dict".parse().unwrap(),
2211                            target: OfferTarget::Collection("modular".parse().unwrap()),
2212                            target_name: "data".parse().unwrap(),
2213                            rights: Some(fio::Operations::CONNECT),
2214                            subdir: ".".parse().unwrap(),
2215                            dependency_type: DependencyType::Strong,
2216                            availability: Availability::Optional,
2217                        })),
2218                        OfferDecl::Storage(OfferStorageDecl {
2219                            source_name: "cache".parse().unwrap(),
2220                            source: OfferSource::Self_,
2221                            target: OfferTarget::Collection("modular".parse().unwrap()),
2222                            target_name: "cache".parse().unwrap(),
2223                            availability: Availability::Required,
2224                        }),
2225                        OfferDecl::Runner(OfferRunnerDecl {
2226                            source: OfferSource::Parent,
2227                            source_name: "elf".parse().unwrap(),
2228                            source_dictionary: "in/dict".parse().unwrap(),
2229                            target: offer_target_static_child("echo"),
2230                            target_name: "elf2".parse().unwrap(),
2231                        }),
2232                        OfferDecl::Resolver(OfferResolverDecl {
2233                            source: OfferSource::Parent,
2234                            source_name: "pkg".parse().unwrap(),
2235                            source_dictionary: "in/dict".parse().unwrap(),
2236                            target: offer_target_static_child("echo"),
2237                            target_name: "pkg".parse().unwrap(),
2238                        }),
2239                        OfferDecl::Service(Box::new(OfferServiceDecl {
2240                            source: OfferSource::Parent,
2241                            source_name: "netstack1".parse().unwrap(),
2242                            source_dictionary: "in/dict".parse().unwrap(),
2243                            source_instance_filter: None,
2244                            renamed_instances: None,
2245                            target: offer_target_static_child("echo"),
2246                            target_name: "mynetstack1".parse().unwrap(),
2247                            availability: Availability::Required,
2248                            dependency_type: Default::default(),
2249                        })),
2250                        OfferDecl::Service(Box::new(OfferServiceDecl {
2251                            source: OfferSource::Parent,
2252                            source_name: "netstack2".parse().unwrap(),
2253                            source_dictionary: ".".parse().unwrap(),
2254                            source_instance_filter: None,
2255                            renamed_instances: None,
2256                            target: offer_target_static_child("echo"),
2257                            target_name: "mynetstack2".parse().unwrap(),
2258                            availability: Availability::Optional,
2259                            dependency_type: Default::default(),
2260                        })),
2261                        OfferDecl::Service(Box::new(OfferServiceDecl {
2262                            source: OfferSource::Parent,
2263                            source_name: "netstack3".parse().unwrap(),
2264                            source_dictionary: ".".parse().unwrap(),
2265                            source_instance_filter: Some(Box::from(["allowedinstance".parse().unwrap()])),
2266                            renamed_instances: Some(Box::from([NameMapping{source_name: "default".parse().unwrap(), target_name: "allowedinstance".parse().unwrap()}])),
2267                            target: offer_target_static_child("echo"),
2268                            target_name: "mynetstack3".parse().unwrap(),
2269                            availability: Availability::Required,
2270                            dependency_type: Default::default(),
2271                        })),
2272                        OfferDecl::Dictionary(OfferDictionaryDecl {
2273                            source: OfferSource::Parent,
2274                            source_name: "bundle".parse().unwrap(),
2275                            source_dictionary: "in/dict".parse().unwrap(),
2276                            target: offer_target_static_child("echo"),
2277                            target_name: "mybundle".parse().unwrap(),
2278                            dependency_type: DependencyType::Weak,
2279                            availability: Availability::Required,
2280                        }),
2281                    ]),
2282                    capabilities: Box::from([
2283                        CapabilityDecl::Service(ServiceDecl {
2284                            name: "netstack".parse().unwrap(),
2285                            source_path: Some("/netstack".parse().unwrap()),
2286                        }),
2287                        CapabilityDecl::Protocol(ProtocolDecl {
2288                            name: "netstack2".parse().unwrap(),
2289                            source_path: Some("/netstack2".parse().unwrap()),
2290                            delivery: DeliveryType::Immediate,
2291                        }),
2292                        CapabilityDecl::Directory(DirectoryDecl {
2293                            name: "data".parse().unwrap(),
2294                            source_path: Some("/data".parse().unwrap()),
2295                            rights: fio::Operations::CONNECT,
2296                        }),
2297                        CapabilityDecl::Storage(StorageDecl {
2298                            name: "cache".parse().unwrap(),
2299                            backing_dir: "data".parse().unwrap(),
2300                            source: StorageDirectorySource::Parent,
2301                            subdir: "cache".parse().unwrap(),
2302                            storage_id: fdecl::StorageId::StaticInstanceId,
2303                        }),
2304                        CapabilityDecl::Runner(RunnerDecl {
2305                            name: "elf".parse().unwrap(),
2306                            source_path: Some("/elf".parse().unwrap()),
2307                        }),
2308                        CapabilityDecl::Resolver(ResolverDecl {
2309                            name: "pkg".parse().unwrap(),
2310                            source_path: Some("/pkg_resolver".parse().unwrap()),
2311                        }),
2312                        CapabilityDecl::Dictionary(DictionaryDecl {
2313                            name: "dict1".parse().unwrap(),
2314                            source_path: None,
2315                        }),
2316                        CapabilityDecl::Dictionary(DictionaryDecl {
2317                            name: "dict2".parse().unwrap(),
2318                            source_path: Some("/in/other".parse().unwrap()),
2319                        }),
2320                    ]),
2321                    children: Box::from([
2322                        ChildDecl {
2323                            name: "netstack".parse().unwrap(),
2324                            url: "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cm".parse().unwrap(),
2325                            startup: fdecl::StartupMode::Lazy,
2326                            on_terminate: None,
2327                            environment: None,
2328                            config_overrides: None,
2329                        },
2330                        ChildDecl {
2331                            name: "gtest".parse().unwrap(),
2332                            url: "fuchsia-pkg://fuchsia.com/gtest#meta/gtest.cm".parse().unwrap(),
2333                            startup: fdecl::StartupMode::Lazy,
2334                            on_terminate: Some(fdecl::OnTerminate::None),
2335                            environment: None,
2336                            config_overrides: None,
2337                        },
2338                        ChildDecl {
2339                            name: "echo".parse().unwrap(),
2340                            url: "fuchsia-pkg://fuchsia.com/echo#meta/echo.cm".parse().unwrap(),
2341                            startup: fdecl::StartupMode::Eager,
2342                            on_terminate: Some(fdecl::OnTerminate::Reboot),
2343                            environment: Some("test_env".parse().unwrap()),
2344                            config_overrides: None,
2345                        },
2346                    ]),
2347                    collections: Box::from([
2348                        CollectionDecl {
2349                            name: "modular".parse().unwrap(),
2350                            durability: fdecl::Durability::Transient,
2351                            environment: None,
2352                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2353                            allow_long_names: true,
2354                            persistent_storage: None,
2355                        },
2356                        CollectionDecl {
2357                            name: "tests".parse().unwrap(),
2358                            durability: fdecl::Durability::Transient,
2359                            environment: Some("test_env".parse().unwrap()),
2360                            allowed_offers: cm_types::AllowedOffers::StaticAndDynamic,
2361                            allow_long_names: true,
2362                            persistent_storage: Some(true),
2363                        },
2364                    ]),
2365                    facets: Some(fdata::Dictionary {
2366                        entries: Some(vec![
2367                            fdata::DictionaryEntry {
2368                                key: "author".to_string(),
2369                                value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
2370                            },
2371                        ]),
2372                        ..Default::default()
2373                    }),
2374                    environments: Box::from([
2375                        EnvironmentDecl {
2376                            name: "test_env".parse().unwrap(),
2377                            extends: fdecl::EnvironmentExtends::Realm,
2378                            runners: Box::from([
2379                                RunnerRegistration {
2380                                    source_name: "runner".parse().unwrap(),
2381                                    source: RegistrationSource::Child("gtest".to_string()),
2382                                    target_name: "gtest-runner".parse().unwrap(),
2383                                }
2384                            ]),
2385                            resolvers: Box::from([
2386                                ResolverRegistration {
2387                                    resolver: "pkg_resolver".parse().unwrap(),
2388                                    source: RegistrationSource::Parent,
2389                                    scheme: "fuchsia-pkg".to_string(),
2390                                }
2391                            ]),
2392                            debug_capabilities: Box::from([
2393                                DebugRegistration::Protocol(DebugProtocolRegistration {
2394                                    source_name: "some_protocol".parse().unwrap(),
2395                                    source: RegistrationSource::Child("gtest".to_string()),
2396                                    target_name: "some_protocol".parse().unwrap(),
2397                                })
2398                            ]),
2399                            stop_timeout_ms: Some(4567),
2400                        }
2401                    ]),
2402                    config: Some(ConfigDecl {
2403                        fields: Box::from([
2404                            ConfigField {
2405                                key: "enable_logging".into(),
2406                                type_: ConfigValueType::Bool,
2407                                mutability: ConfigMutability::default(),
2408                            }
2409                        ]),
2410                        checksum: ConfigChecksum::Sha256([
2411                            0x64, 0x49, 0x9E, 0x75, 0xF3, 0x37, 0x69, 0x88, 0x74, 0x3B, 0x38, 0x16,
2412                            0xCD, 0x14, 0x70, 0x9F, 0x3D, 0x4A, 0xD3, 0xE2, 0x24, 0x9A, 0x1A, 0x34,
2413                            0x80, 0xB4, 0x9E, 0xB9, 0x63, 0x57, 0xD6, 0xED,
2414                        ]),
2415                        value_source: ConfigValueSource::PackagePath("fake.cvf".into())
2416                    }),
2417                    debug_info: None,
2418                }
2419            },
2420        },
2421    }
2422
2423    test_fidl_into_and_from! {
2424        fidl_into_and_from_use_source => {
2425            input = vec![
2426                fdecl::Ref::Parent(fdecl::ParentRef{}),
2427                fdecl::Ref::Framework(fdecl::FrameworkRef{}),
2428                fdecl::Ref::Debug(fdecl::DebugRef{}),
2429                fdecl::Ref::Capability(fdecl::CapabilityRef {name: "capability".to_string()}),
2430                fdecl::Ref::Child(fdecl::ChildRef {
2431                    name: "foo".into(),
2432                    collection: None,
2433                }),
2434                fdecl::Ref::Environment(fdecl::EnvironmentRef{}),
2435            ],
2436            input_type = fdecl::Ref,
2437            result = vec![
2438                UseSource::Parent,
2439                UseSource::Framework,
2440                UseSource::Debug,
2441                UseSource::Capability("capability".parse().unwrap()),
2442                UseSource::Child("foo".parse().unwrap()),
2443                UseSource::Environment,
2444            ],
2445            result_type = UseSource,
2446        },
2447        fidl_into_and_from_expose_source => {
2448            input = vec![
2449                fdecl::Ref::Self_(fdecl::SelfRef {}),
2450                fdecl::Ref::Child(fdecl::ChildRef {
2451                    name: "foo".into(),
2452                    collection: None,
2453                }),
2454                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2455                fdecl::Ref::Collection(fdecl::CollectionRef { name: "foo".to_string() }),
2456            ],
2457            input_type = fdecl::Ref,
2458            result = vec![
2459                ExposeSource::Self_,
2460                ExposeSource::Child("foo".parse().unwrap()),
2461                ExposeSource::Framework,
2462                ExposeSource::Collection("foo".parse().unwrap()),
2463            ],
2464            result_type = ExposeSource,
2465        },
2466        fidl_into_and_from_offer_source => {
2467            input = vec![
2468                fdecl::Ref::Self_(fdecl::SelfRef {}),
2469                fdecl::Ref::Child(fdecl::ChildRef {
2470                    name: "foo".into(),
2471                    collection: None,
2472                }),
2473                fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2474                fdecl::Ref::Capability(fdecl::CapabilityRef { name: "foo".to_string() }),
2475                fdecl::Ref::Parent(fdecl::ParentRef {}),
2476                fdecl::Ref::Collection(fdecl::CollectionRef { name: "foo".to_string() }),
2477                fdecl::Ref::VoidType(fdecl::VoidRef {}),
2478            ],
2479            input_type = fdecl::Ref,
2480            result = vec![
2481                OfferSource::Self_,
2482                offer_source_static_child("foo"),
2483                OfferSource::Framework,
2484                OfferSource::Capability("foo".parse().unwrap()),
2485                OfferSource::Parent,
2486                OfferSource::Collection("foo".parse().unwrap()),
2487                OfferSource::Void,
2488            ],
2489            result_type = OfferSource,
2490        },
2491        fidl_into_and_from_dictionary_source => {
2492            input = vec![
2493                fdecl::Ref::Self_(fdecl::SelfRef {}),
2494                fdecl::Ref::Child(fdecl::ChildRef {
2495                    name: "foo".into(),
2496                    collection: None,
2497                }),
2498                fdecl::Ref::Parent(fdecl::ParentRef {}),
2499            ],
2500            input_type = fdecl::Ref,
2501            result = vec![
2502                DictionarySource::Self_,
2503                DictionarySource::Child(ChildRef {
2504                    name: "foo".parse().unwrap(),
2505                    collection: None,
2506                }),
2507                DictionarySource::Parent,
2508            ],
2509            result_type = DictionarySource,
2510        },
2511
2512        fidl_into_and_from_capability_without_path => {
2513            input = vec![
2514                fdecl::Protocol {
2515                    name: Some("foo_protocol".to_string()),
2516                    source_path: None,
2517                    delivery: Some(fdecl::DeliveryType::Immediate),
2518                    ..Default::default()
2519                },
2520            ],
2521            input_type = fdecl::Protocol,
2522            result = vec![
2523                ProtocolDecl {
2524                    name: "foo_protocol".parse().unwrap(),
2525                    source_path: None,
2526                    delivery: DeliveryType::Immediate,
2527                }
2528            ],
2529            result_type = ProtocolDecl,
2530        },
2531        fidl_into_and_from_storage_capability => {
2532            input = vec![
2533                fdecl::Storage {
2534                    name: Some("minfs".to_string()),
2535                    backing_dir: Some("minfs".into()),
2536                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2537                        name: "foo".into(),
2538                        collection: None,
2539                    })),
2540                    subdir: None,
2541                    storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
2542                    ..Default::default()
2543                },
2544            ],
2545            input_type = fdecl::Storage,
2546            result = vec![
2547                StorageDecl {
2548                    name: "minfs".parse().unwrap(),
2549                    backing_dir: "minfs".parse().unwrap(),
2550                    source: StorageDirectorySource::Child("foo".to_string()),
2551                    subdir: ".".parse().unwrap(),
2552                    storage_id: fdecl::StorageId::StaticInstanceIdOrMoniker,
2553                },
2554            ],
2555            result_type = StorageDecl,
2556        },
2557        fidl_into_and_from_storage_capability_restricted => {
2558            input = vec![
2559                fdecl::Storage {
2560                    name: Some("minfs".to_string()),
2561                    backing_dir: Some("minfs".into()),
2562                    source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2563                        name: "foo".into(),
2564                        collection: None,
2565                    })),
2566                    subdir: None,
2567                    storage_id: Some(fdecl::StorageId::StaticInstanceId),
2568                    ..Default::default()
2569                },
2570            ],
2571            input_type = fdecl::Storage,
2572            result = vec![
2573                StorageDecl {
2574                    name: "minfs".parse().unwrap(),
2575                    backing_dir: "minfs".parse().unwrap(),
2576                    source: StorageDirectorySource::Child("foo".to_string()),
2577                    subdir: ".".parse().unwrap(),
2578                    storage_id: fdecl::StorageId::StaticInstanceId,
2579                },
2580            ],
2581            result_type = StorageDecl,
2582        },
2583    }
2584
2585    test_fidl_into! {
2586        all_with_omitted_defaults => {
2587            input = fdecl::Component {
2588                program: Some(fdecl::Program {
2589                    runner: Some("elf".to_string()),
2590                    info: Some(fdata::Dictionary {
2591                        entries: Some(vec![]),
2592                        ..Default::default()
2593                    }),
2594                    ..Default::default()
2595                }),
2596                uses: Some(vec![]),
2597                exposes: Some(vec![]),
2598                offers: Some(vec![]),
2599                capabilities: Some(vec![]),
2600                children: Some(vec![]),
2601                collections: Some(vec![
2602                     fdecl::Collection {
2603                         name: Some("modular".to_string()),
2604                         durability: Some(fdecl::Durability::Transient),
2605                         environment: None,
2606                         allowed_offers: None,
2607                         allow_long_names: None,
2608                         persistent_storage: None,
2609                         ..Default::default()
2610                     },
2611                     fdecl::Collection {
2612                         name: Some("tests".to_string()),
2613                         durability: Some(fdecl::Durability::Transient),
2614                         environment: Some("test_env".to_string()),
2615                         allowed_offers: Some(fdecl::AllowedOffers::StaticOnly),
2616                         allow_long_names: None,
2617                         persistent_storage: Some(false),
2618                         ..Default::default()
2619                     },
2620                     fdecl::Collection {
2621                         name: Some("dyn_offers".to_string()),
2622                         durability: Some(fdecl::Durability::Transient),
2623                         allowed_offers: Some(fdecl::AllowedOffers::StaticAndDynamic),
2624                         allow_long_names: None,
2625                         persistent_storage: Some(true),
2626                         ..Default::default()
2627                     },
2628                     fdecl::Collection {
2629                         name: Some("long_child_names".to_string()),
2630                         durability: Some(fdecl::Durability::Transient),
2631                         allowed_offers: None,
2632                         allow_long_names: Some(true),
2633                         persistent_storage: None,
2634                         ..Default::default()
2635                     },
2636                ]),
2637                facets: Some(fdata::Dictionary{
2638                    entries: Some(vec![]),
2639                    ..Default::default()
2640                }),
2641                environments: Some(vec![]),
2642                ..Default::default()
2643            },
2644            result = {
2645                ComponentDecl {
2646                    program: Some(ProgramDecl {
2647                        runner: Some("elf".parse().unwrap()),
2648                        info: fdata::Dictionary {
2649                            entries: Some(vec![]),
2650                            ..Default::default()
2651                        },
2652                    }),
2653                    uses: Box::from([]),
2654                    exposes: Box::from([]),
2655                    offers: Box::from([]),
2656                    capabilities: Box::from([]),
2657                    children: Box::from([]),
2658                    collections: Box::from([
2659                        CollectionDecl {
2660                            name: "modular".parse().unwrap(),
2661                            durability: fdecl::Durability::Transient,
2662                            environment: None,
2663                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2664                            allow_long_names: false,
2665                            persistent_storage: None,
2666                        },
2667                        CollectionDecl {
2668                            name: "tests".parse().unwrap(),
2669                            durability: fdecl::Durability::Transient,
2670                            environment: Some("test_env".parse().unwrap()),
2671                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2672                            allow_long_names: false,
2673                            persistent_storage: Some(false),
2674                        },
2675                        CollectionDecl {
2676                            name: "dyn_offers".parse().unwrap(),
2677                            durability: fdecl::Durability::Transient,
2678                            environment: None,
2679                            allowed_offers: cm_types::AllowedOffers::StaticAndDynamic,
2680                            allow_long_names: false,
2681                            persistent_storage: Some(true),
2682                        },
2683                        CollectionDecl {
2684                            name: "long_child_names".parse().unwrap(),
2685                            durability: fdecl::Durability::Transient,
2686                            environment: None,
2687                            allowed_offers: cm_types::AllowedOffers::StaticOnly,
2688                            allow_long_names: true,
2689                            persistent_storage: None,
2690                        },
2691                    ]),
2692                    facets: Some(fdata::Dictionary{
2693                        entries: Some(vec![]),
2694                        ..Default::default()
2695                    }),
2696                    environments: Box::from([]),
2697                    config: None,
2698                    debug_info: None,
2699                }
2700            },
2701        },
2702    }
2703
2704    #[test]
2705    fn default_expose_availability() {
2706        let source = fdecl::Ref::Self_(fdecl::SelfRef {});
2707        let source_name = "source";
2708        let target = fdecl::Ref::Parent(fdecl::ParentRef {});
2709        let target_name = "target";
2710        let expose_service: ExposeServiceDecl = fdecl::ExposeService {
2711            source: Some(source.clone()),
2712            source_name: Some(source_name.into()),
2713            target: Some(target.clone()),
2714            target_name: Some(target_name.into()),
2715            availability: None,
2716            ..Default::default()
2717        }
2718        .fidl_into_native();
2719        assert_eq!(*expose_service.availability(), Availability::Required);
2720
2721        let expose_protocol: ExposeProtocolDecl = fdecl::ExposeProtocol {
2722            source: Some(source.clone()),
2723            source_name: Some(source_name.into()),
2724            target: Some(target.clone()),
2725            target_name: Some(target_name.into()),
2726            ..Default::default()
2727        }
2728        .fidl_into_native();
2729        assert_eq!(*expose_protocol.availability(), Availability::Required);
2730
2731        let expose_directory: ExposeDirectoryDecl = fdecl::ExposeDirectory {
2732            source: Some(source.clone()),
2733            source_name: Some(source_name.into()),
2734            target: Some(target.clone()),
2735            target_name: Some(target_name.into()),
2736            ..Default::default()
2737        }
2738        .fidl_into_native();
2739        assert_eq!(*expose_directory.availability(), Availability::Required);
2740
2741        let expose_runner: ExposeRunnerDecl = fdecl::ExposeRunner {
2742            source: Some(source.clone()),
2743            source_name: Some(source_name.into()),
2744            target: Some(target.clone()),
2745            target_name: Some(target_name.into()),
2746            ..Default::default()
2747        }
2748        .fidl_into_native();
2749        assert_eq!(*expose_runner.availability(), Availability::Required);
2750
2751        let expose_resolver: ExposeResolverDecl = fdecl::ExposeResolver {
2752            source: Some(source.clone()),
2753            source_name: Some(source_name.into()),
2754            target: Some(target.clone()),
2755            target_name: Some(target_name.into()),
2756            ..Default::default()
2757        }
2758        .fidl_into_native();
2759        assert_eq!(*expose_resolver.availability(), Availability::Required);
2760
2761        let expose_dictionary: ExposeDictionaryDecl = fdecl::ExposeDictionary {
2762            source: Some(source.clone()),
2763            source_name: Some(source_name.into()),
2764            target: Some(target.clone()),
2765            target_name: Some(target_name.into()),
2766            ..Default::default()
2767        }
2768        .fidl_into_native();
2769        assert_eq!(*expose_dictionary.availability(), Availability::Required);
2770    }
2771
2772    #[test]
2773    fn default_delivery_type() {
2774        let protocol: ProtocolDecl = fdecl::Protocol {
2775            name: Some("foo".to_string()),
2776            source_path: Some("/foo".to_string()),
2777            delivery: None,
2778            ..Default::default()
2779        }
2780        .fidl_into_native();
2781        assert_eq!(protocol.delivery, DeliveryType::Immediate)
2782    }
2783
2784    #[test]
2785    fn on_readable_delivery_type() {
2786        let protocol: ProtocolDecl = fdecl::Protocol {
2787            name: Some("foo".to_string()),
2788            source_path: Some("/foo".to_string()),
2789            delivery: Some(fdecl::DeliveryType::OnReadable),
2790            ..Default::default()
2791        }
2792        .fidl_into_native();
2793        assert_eq!(protocol.delivery, DeliveryType::OnReadable)
2794    }
2795
2796    #[test]
2797    fn config_value_matches_type() {
2798        let bool_true = ConfigValue::Single(ConfigSingleValue::Bool(true));
2799        let bool_false = ConfigValue::Single(ConfigSingleValue::Bool(false));
2800        let uint8_zero = ConfigValue::Single(ConfigSingleValue::Uint8(0));
2801        let vec_bool_true = ConfigValue::Vector(ConfigVectorValue::BoolVector(Box::from([true])));
2802        let vec_bool_false = ConfigValue::Vector(ConfigVectorValue::BoolVector(Box::from([false])));
2803
2804        assert!(bool_true.matches_type(&bool_false));
2805        assert!(vec_bool_true.matches_type(&vec_bool_false));
2806
2807        assert!(!bool_true.matches_type(&uint8_zero));
2808        assert!(!bool_true.matches_type(&vec_bool_true));
2809    }
2810}