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