Skip to main content

cm_rust/
lib.rs

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