Skip to main content

cml/
translate.rs

1// Copyright 2020 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 crate::error::Error;
6use crate::features::{Feature, FeatureSet};
7use crate::types::child::{Child, ContextChild};
8use crate::types::collection::{Collection, ContextCollection};
9use crate::types::common::ContextCapabilityClause;
10use crate::types::document::{Document, DocumentContext};
11use crate::types::environment::{
12    ContextDebugRegistration, ContextEnvironment, ContextResolverRegistration,
13    ContextRunnerRegistration, DebugRegistration, Environment, EnvironmentExtends, EnvironmentRef,
14    RunnerRegistration,
15};
16use crate::types::expose::{ContextExpose, Expose, ExposeFromRef, ExposeToRef};
17use crate::types::offer::{
18    ContextOffer, Offer, OfferFromRef, OfferToRef, TargetAvailability,
19    offer_to_all_would_duplicate, offer_to_all_would_duplicate_context,
20};
21use crate::types::program::ContextProgram;
22use crate::types::right::RightsClause;
23use crate::types::r#use::{ContextUse, Use, UseFromRef};
24use crate::validate::CapabilityRequirements;
25use crate::{
26    AnyRef, AsClause, AsClauseContext, Availability, Capability, CapabilityClause, ConfigKey,
27    ConfigNestedValueType, ConfigRuntimeSource, ConfigType, ConfigValueType, ContextCapability,
28    ContextPathClause, ContextSpanned, DictionaryRef, EventScope, FromClause, FromClauseContext,
29    OneOrMany, Path, PathClause, Program, ResolverRegistration, RootDictionaryRef,
30    SourceAvailability, validate,
31};
32use cm_rust::NativeIntoFidl;
33use cm_types::{self as cm, BorrowedName, Name, StartupMode};
34use directed_graph::DirectedGraph;
35use indexmap::IndexMap;
36use itertools::Itertools;
37use serde_json::{Map, Value};
38use sha2::{Digest, Sha256};
39use std::collections::{BTreeMap, BTreeSet};
40use std::convert::{Into, TryInto};
41use std::path::PathBuf;
42use std::sync::Arc;
43use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio};
44
45/// Options for CML compilation. Uses the builder pattern.
46#[derive(Default, Clone)]
47pub struct CompileOptions<'a> {
48    file: Option<PathBuf>,
49    config_package_path: Option<String>,
50    features: Option<&'a FeatureSet>,
51    capability_requirements: CapabilityRequirements<'a>,
52}
53
54impl<'a> CompileOptions<'a> {
55    pub fn new() -> Self {
56        Default::default()
57    }
58
59    /// The path to the CML file, if applicable. Used for error reporting.
60    pub fn file(mut self, file: &std::path::Path) -> CompileOptions<'a> {
61        self.file = Some(file.to_path_buf());
62        self
63    }
64
65    /// The path within the component's package at which to find config value files.
66    pub fn config_package_path(mut self, config_package_path: &str) -> CompileOptions<'a> {
67        self.config_package_path = Some(config_package_path.to_string());
68        self
69    }
70
71    /// Which additional features are enabled. Defaults to none.
72    pub fn features(mut self, features: &'a FeatureSet) -> CompileOptions<'a> {
73        self.features = Some(features);
74        self
75    }
76
77    /// Require that the component must use or offer particular protocols. Defaults to no
78    /// requirements.
79    pub fn protocol_requirements(
80        mut self,
81        protocol_requirements: CapabilityRequirements<'a>,
82    ) -> CompileOptions<'a> {
83        self.capability_requirements = protocol_requirements;
84        self
85    }
86}
87
88/// Compiles the [Document] into a FIDL [fdecl::Component].
89/// `options` is a builder used to provide additional options, such as file path for debugging
90/// purposes.
91///
92/// Note: This function ignores the `include` section of the document. It is
93/// assumed that those entries were already processed.
94pub fn compile(
95    document: &Document,
96    options: CompileOptions<'_>,
97) -> Result<fdecl::Component, Error> {
98    validate::validate_cml(
99        &document,
100        options.file.as_ref().map(PathBuf::as_path),
101        options.features.unwrap_or(&FeatureSet::empty()),
102        &options.capability_requirements,
103    )?;
104
105    let all_capability_names: BTreeSet<&BorrowedName> =
106        document.all_capability_names().into_iter().collect();
107    let all_children = document.all_children_names().into_iter().collect();
108    let all_collections = document.all_collection_names().into_iter().collect();
109    let component = fdecl::Component {
110        program: document.program.as_ref().map(translate_program_deprecated).transpose()?,
111        uses: document
112            .r#use
113            .as_ref()
114            .map(|u| {
115                translate_use_deprecated(
116                    &options,
117                    u,
118                    &all_capability_names,
119                    &all_children,
120                    &all_collections,
121                )
122            })
123            .transpose()?,
124        exposes: document
125            .expose
126            .as_ref()
127            .map(|e| {
128                translate_expose_deprecated(
129                    &options,
130                    e,
131                    &all_capability_names,
132                    &all_collections,
133                    &all_children,
134                )
135            })
136            .transpose()?,
137        offers: document
138            .offer
139            .as_ref()
140            .map(|offer| {
141                translate_offer_deprecated(
142                    &options,
143                    offer,
144                    &all_capability_names,
145                    &all_children,
146                    &all_collections,
147                )
148            })
149            .transpose()?,
150        capabilities: document
151            .capabilities
152            .as_ref()
153            .map(|c| translate_capabilities_deprecated(&options, c, false))
154            .transpose()?,
155        children: document.children.as_ref().map(translate_children_deprecated),
156        collections: document.collections.as_ref().map(translate_collections_deprecated),
157        environments: document
158            .environments
159            .as_ref()
160            .map(|env| translate_environments_deprecated(&options, env, &all_capability_names))
161            .transpose()?,
162        facets: document.facets.clone().map(dictionary_from_nested_map).transpose()?,
163        config: translate_config_deprecated(
164            &document.config,
165            &document.r#use,
166            &options.config_package_path,
167        )?,
168        ..Default::default()
169    };
170
171    let mut deps = DirectedGraph::new();
172    cm_fidl_validator::validate(&component, &mut deps).map_err(Error::fidl_validator)?;
173
174    Ok(component)
175}
176
177/// Compiles the [DocumentContext] into a FIDL [fdecl::Component].
178pub fn compile_context(
179    document: &DocumentContext,
180    options: CompileOptions<'_>,
181) -> Result<fdecl::Component, Error> {
182    validate::validate_cml_context(
183        &document,
184        options.features.unwrap_or(&FeatureSet::empty()),
185        &options.capability_requirements,
186    )?;
187
188    let all_capability_names: BTreeSet<Name> =
189        document.all_capability_names().iter().cloned().collect();
190    let all_children: BTreeSet<Name> = document.all_children_names().iter().cloned().collect();
191    let all_collections: BTreeSet<Name> = document.all_collection_names().iter().cloned().collect();
192
193    let component = fdecl::Component {
194        program: document.program.as_ref().map(|p| translate_program(&p.value)).transpose()?,
195
196        uses: document
197            .r#use
198            .as_ref()
199            .map(|u| {
200                translate_use(&options, u, &all_capability_names, &all_children, &all_collections)
201            })
202            .transpose()?,
203
204        exposes: document
205            .expose
206            .as_ref()
207            .map(|e| {
208                translate_expose(
209                    &options,
210                    e,
211                    &all_capability_names,
212                    &all_collections,
213                    &all_children,
214                )
215            })
216            .transpose()?,
217
218        offers: document
219            .offer
220            .as_ref()
221            .map(|o| {
222                translate_offer(&options, o, &all_capability_names, &all_children, &all_collections)
223            })
224            .transpose()?,
225
226        capabilities: document
227            .capabilities
228            .as_ref()
229            .map(|c| translate_capabilities(&options, c, false))
230            .transpose()?,
231
232        children: document.children.as_ref().map(|c| translate_children(c)),
233        collections: document.collections.as_ref().map(|c| translate_collections(c)),
234
235        environments: document
236            .environments
237            .as_ref()
238            .map(|env| translate_environments(&options, env, &all_capability_names))
239            .transpose()?,
240        facets: document.facets.clone().map(dictionary_from_nested_spanned_map).transpose()?,
241
242        config: translate_config(&document.config, &document.r#use, &options.config_package_path)?,
243        ..Default::default()
244    };
245
246    let mut deps = DirectedGraph::new();
247    cm_fidl_validator::validate(&component, &mut deps).map_err(Error::fidl_validator)?;
248
249    Ok(component)
250}
251
252/// Converts an IndexMap of spanned Values into a fuchsia.data.Dictionary.
253pub fn dictionary_from_context_map(
254    map: IndexMap<String, ContextSpanned<Value>>,
255) -> Result<fdata::Dictionary, Error> {
256    let mut entries = Vec::new();
257
258    for (key, spanned_value) in map {
259        // We pass each value's specific origin into the converter
260        let dictionary_value =
261            value_to_dictionary_value(spanned_value.value, &spanned_value.origin)?;
262
263        entries.push(fdata::DictionaryEntry { key, value: dictionary_value });
264    }
265
266    // Fuchsia dictionaries should be sorted lexicographically by key
267    // to ensure the resulting .cm file is deterministic.
268    entries.sort_by(|a, b| a.key.cmp(&b.key));
269
270    Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
271}
272
273// Converts a Map<String, serde_json::Value> to a fuchsia Dictionary.
274fn dictionary_from_map(in_obj: Map<String, Value>) -> Result<fdata::Dictionary, Error> {
275    let mut entries = vec![];
276    for (key, v) in in_obj {
277        let value = value_to_dictionary_value_deprecated(v)?;
278        entries.push(fdata::DictionaryEntry { key, value });
279    }
280    Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
281}
282
283// Converts a serde_json::Value into a fuchsia DictionaryValue.
284fn value_to_dictionary_value_deprecated(
285    value: Value,
286) -> Result<Option<Box<fdata::DictionaryValue>>, Error> {
287    match value {
288        Value::Null => Ok(None),
289        Value::String(s) => Ok(Some(Box::new(fdata::DictionaryValue::Str(s)))),
290        Value::Array(arr) => {
291            if arr.iter().all(Value::is_string) {
292                let strs =
293                    arr.into_iter().map(|v| v.as_str().unwrap().to_owned()).collect::<Vec<_>>();
294                Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs))))
295            } else if arr.iter().all(Value::is_object) {
296                let objs = arr
297                    .into_iter()
298                    .map(|v| v.as_object().unwrap().clone())
299                    .map(|v| dictionary_from_nested_map(v.into_iter().collect()))
300                    .collect::<Result<Vec<_>, _>>()?;
301                Ok(Some(Box::new(fdata::DictionaryValue::ObjVec(objs))))
302            } else {
303                Err(Error::validate(
304                    "Values of an array must either exclusively strings or exclusively objects",
305                ))
306            }
307        }
308        other => Err(Error::validate(format!(
309            "Value must be string, list of strings, or list of objects: {:?}",
310            other
311        ))),
312    }
313}
314
315fn value_to_dictionary_value(
316    value: Value,
317    origin: &Arc<PathBuf>,
318) -> Result<Option<Box<fdata::DictionaryValue>>, Error> {
319    match value {
320        Value::Null => Ok(None),
321        Value::String(s) => Ok(Some(Box::new(fdata::DictionaryValue::Str(s)))),
322        Value::Array(arr) => {
323            if arr.is_empty() {
324                return Ok(Some(Box::new(fdata::DictionaryValue::StrVec(vec![]))));
325            }
326
327            if arr.iter().all(Value::is_string) {
328                let strs =
329                    arr.into_iter().map(|v| v.as_str().unwrap().to_owned()).collect::<Vec<_>>();
330                Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs))))
331            } else if arr.iter().all(Value::is_object) {
332                let objs = arr
333                    .into_iter()
334                    .map(|v| {
335                        let obj_map = v.as_object().unwrap().clone().into_iter().collect();
336                        dictionary_from_nested_map(obj_map)
337                    })
338                    .collect::<Result<Vec<_>, _>>()?;
339                Ok(Some(Box::new(fdata::DictionaryValue::ObjVec(objs))))
340            } else {
341                Err(Error::validate_context(
342                    "Values of an array must be exclusively strings or exclusively objects",
343                    Some(origin.clone()),
344                ))
345            }
346        }
347        other => Err(Error::validate_context(
348            format!("Value must be string, list of strings, or list of objects: {:?}", other),
349            Some(origin.clone()),
350        )),
351    }
352}
353
354/// Converts a [`serde_json::Map<String, serde_json::Value>`] to a [`fuchsia.data.Dictionary`].
355///
356/// The JSON object is converted as follows:
357///
358/// * Convert all non-string and string values into DictionaryValue::str.
359/// * Flatten nested objects into top-level keys delimited by ".".
360/// * Convert array of discrete values into  array of DictionaryValue::str_vec.
361/// * Convert array of objects into array of DictionaryValue::obj_vec.
362///
363/// Values may be null, strings, arrays of strings, arrays of objects, or objects.
364///
365/// # Example
366///
367/// ```json
368/// {
369///   "binary": "bin/app",
370///   "lifecycle": {
371///     "stop_event": "notify",
372///     "nested": {
373///       "foo": "bar"
374///     }
375///   }
376/// }
377/// ```
378///
379/// is flattened to:
380///
381/// ```json
382/// {
383///   "binary": "bin/app",
384///   "lifecycle.stop_event": "notify",
385///   "lifecycle.nested.foo": "bar"
386/// }
387/// ```
388fn dictionary_from_nested_map(map: IndexMap<String, Value>) -> Result<fdata::Dictionary, Error> {
389    fn key_value_to_entries(
390        key: String,
391        value: Value,
392    ) -> Result<Vec<fdata::DictionaryEntry>, Error> {
393        if let Value::Object(map) = value {
394            let entries = map
395                .into_iter()
396                .map(|(k, v)| key_value_to_entries([key.clone(), ".".to_string(), k].concat(), v))
397                .collect::<Result<Vec<_>, _>>()?
398                .into_iter()
399                .flatten()
400                .collect();
401            return Ok(entries);
402        }
403
404        let entry_value = value_to_dictionary_value_deprecated(value)?;
405        Ok(vec![fdata::DictionaryEntry { key, value: entry_value }])
406    }
407
408    let entries = map
409        .into_iter()
410        .map(|(k, v)| key_value_to_entries(k, v))
411        .collect::<Result<Vec<_>, _>>()?
412        .into_iter()
413        .flatten()
414        .collect();
415    Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
416}
417
418fn dictionary_from_nested_spanned_map(
419    map: IndexMap<String, ContextSpanned<Value>>,
420) -> Result<fdata::Dictionary, Error> {
421    fn key_value_to_entries(
422        key: String,
423        value: Value,
424    ) -> Result<Vec<fdata::DictionaryEntry>, Error> {
425        if let Value::Object(map) = value {
426            let entries = map
427                .into_iter()
428                .map(|(k, v)| key_value_to_entries([key.clone(), ".".to_string(), k].concat(), v))
429                .collect::<Result<Vec<_>, _>>()?
430                .into_iter()
431                .flatten()
432                .collect();
433            return Ok(entries);
434        }
435
436        let entry_value = value_to_dictionary_value_deprecated(value)?;
437        Ok(vec![fdata::DictionaryEntry { key, value: entry_value }])
438    }
439
440    let entries = map
441        .into_iter()
442        .map(|(k, v)| key_value_to_entries(k, v.value))
443        .collect::<Result<Vec<_>, _>>()?
444        .into_iter()
445        .flatten()
446        .collect();
447    Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
448}
449
450/// Translates a [`Program`] to a [`fuchsia.component.decl/Program`].
451fn translate_program_deprecated(program: &Program) -> Result<fdecl::Program, Error> {
452    Ok(fdecl::Program {
453        runner: program.runner.as_ref().map(|r| r.to_string()),
454        info: Some(dictionary_from_nested_map(program.info.clone())?),
455        ..Default::default()
456    })
457}
458
459/// Translates a [`ContextProgram`] to a [`fuchsia.component.decl/Program`].
460fn translate_program(program: &ContextProgram) -> Result<fdecl::Program, Error> {
461    Ok(fdecl::Program {
462        runner: program.runner.as_ref().map(|r| r.value.clone().into()),
463        info: Some(dictionary_from_nested_map(program.info.clone())?),
464        ..Default::default()
465    })
466}
467
468/// `use` rules consume a single capability from one source (parent|framework).
469fn translate_use_deprecated(
470    options: &CompileOptions<'_>,
471    use_in: &Vec<Use>,
472    all_capability_names: &BTreeSet<&BorrowedName>,
473    all_children: &BTreeSet<&BorrowedName>,
474    all_collections: &BTreeSet<&BorrowedName>,
475) -> Result<Vec<fdecl::Use>, Error> {
476    let mut out_uses = vec![];
477    for use_ in use_in {
478        if let Some(n) = use_.service() {
479            let (source, source_dictionary) = extract_use_source_deprecated(
480                options,
481                use_,
482                all_capability_names,
483                all_children,
484                Some(all_collections),
485            )?;
486            let target_paths = all_target_use_paths_deprecated(use_, use_)
487                .ok_or_else(|| Error::internal("no capability"))?;
488            let source_names = n.into_iter();
489            let availability = extract_use_availability_deprecated(use_)?;
490            for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
491            {
492                out_uses.push(fdecl::Use::Service(fdecl::UseService {
493                    source: Some(source.clone()),
494                    source_name: Some(source_name.to_string()),
495                    source_dictionary: source_dictionary.clone(),
496                    target_path: Some(target_path.to_string()),
497                    dependency_type: Some(
498                        use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
499                    ),
500                    availability: Some(availability),
501                    ..Default::default()
502                }));
503            }
504        } else if let Some(n) = use_.protocol() {
505            let (source, source_dictionary) = extract_use_source_deprecated(
506                options,
507                use_,
508                all_capability_names,
509                all_children,
510                None,
511            )?;
512            let availability = extract_use_availability_deprecated(use_)?;
513            if use_.numbered_handle.is_some() {
514                let OneOrMany::One(source_name) = n else {
515                    panic!("numbered_handle: multiple source_name");
516                };
517                out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
518                    source: Some(source.clone()),
519                    source_name: Some(source_name.to_string()),
520                    source_dictionary: source_dictionary.clone(),
521                    target_path: None,
522                    numbered_handle: use_.numbered_handle.map(Into::into),
523                    dependency_type: Some(
524                        use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
525                    ),
526                    availability: Some(availability),
527                    ..Default::default()
528                }));
529                continue;
530            }
531            let source_names = n.into_iter();
532            let target_paths = all_target_use_paths_deprecated(use_, use_)
533                .ok_or_else(|| Error::internal("no capability"))?;
534            for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
535            {
536                out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
537                    source: Some(source.clone()),
538                    source_name: Some(source_name.to_string()),
539                    source_dictionary: source_dictionary.clone(),
540                    target_path: Some(target_path.into()),
541                    numbered_handle: use_.numbered_handle.map(Into::into),
542                    dependency_type: Some(
543                        use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
544                    ),
545                    availability: Some(availability),
546                    ..Default::default()
547                }));
548            }
549        } else if let Some(n) = &use_.directory {
550            let (source, source_dictionary) = extract_use_source_deprecated(
551                options,
552                use_,
553                all_capability_names,
554                all_children,
555                None,
556            )?;
557            let target_path = one_target_use_path_deprecated(use_, use_)?;
558            let rights = extract_required_rights(use_, "use")?;
559            let subdir = extract_use_subdir_deprecated(use_);
560            let availability = extract_use_availability_deprecated(use_)?;
561            out_uses.push(fdecl::Use::Directory(fdecl::UseDirectory {
562                source: Some(source),
563                source_name: Some(n.clone().into()),
564                source_dictionary,
565                target_path: Some(target_path.into()),
566                rights: Some(rights),
567                subdir: subdir.map(|s| s.into()),
568                dependency_type: Some(
569                    use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
570                ),
571                availability: Some(availability),
572                ..Default::default()
573            }));
574        } else if let Some(n) = &use_.storage {
575            let target_path = one_target_use_path_deprecated(use_, use_)?;
576            let availability = extract_use_availability_deprecated(use_)?;
577            out_uses.push(fdecl::Use::Storage(fdecl::UseStorage {
578                source_name: Some(n.clone().into()),
579                target_path: Some(target_path.into()),
580                availability: Some(availability),
581                ..Default::default()
582            }));
583        } else if let Some(names) = &use_.event_stream {
584            let source_names: Vec<String> =
585                annotate_type::<Vec<cm_types::Name>>(names.clone().into())
586                    .iter()
587                    .map(|name| name.to_string())
588                    .collect();
589            let availability = extract_use_availability_deprecated(use_)?;
590            for name in source_names {
591                let scopes = match use_.scope.clone() {
592                    Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
593                    None => None,
594                };
595                let internal_error = format!(
596                    "Internal error in all_target_use_paths_deprecated when translating an EventStream. \
597                    Please file a bug."
598                );
599                let (source, _source_dictionary) = extract_use_source_deprecated(
600                    options,
601                    use_,
602                    all_capability_names,
603                    all_children,
604                    None,
605                )?;
606                out_uses.push(fdecl::Use::EventStream(fdecl::UseEventStream {
607                    source_name: Some(name),
608                    scope: match scopes {
609                        Some(values) => {
610                            let mut output = vec![];
611                            for value in &values {
612                                static EMPTY_SET: BTreeSet<&BorrowedName> = BTreeSet::new();
613                                if let Some(target) = translate_target_ref_deprecated(
614                                    options,
615                                    value.into(),
616                                    &all_children,
617                                    &all_collections,
618                                    &EMPTY_SET,
619                                    Some(&TargetAvailability::Required),
620                                )? {
621                                    output.push(target);
622                                }
623                            }
624                            Some(output)
625                        }
626                        None => None,
627                    },
628                    source: Some(source),
629                    target_path: Some(
630                        annotate_type::<Vec<cm_types::Path>>(
631                            all_target_use_paths_deprecated(use_, use_)
632                                .ok_or_else(|| Error::internal(internal_error.clone()))?
633                                .into(),
634                        )
635                        .iter()
636                        .next()
637                        .ok_or_else(|| Error::internal(internal_error.clone()))?
638                        .to_string(),
639                    ),
640                    filter: match use_.filter.clone() {
641                        Some(dict) => Some(dictionary_from_map(dict)?),
642                        None => None,
643                    },
644                    availability: Some(availability),
645                    ..Default::default()
646                }));
647            }
648        } else if let Some(n) = &use_.runner {
649            let (source, source_dictionary) = extract_use_source_deprecated(
650                &options,
651                use_,
652                all_capability_names,
653                all_children,
654                None,
655            )?;
656            #[cfg(fuchsia_api_level_at_least = "HEAD")]
657            out_uses.push(fdecl::Use::Runner(fdecl::UseRunner {
658                source: Some(source),
659                source_name: Some(n.clone().into()),
660                source_dictionary,
661                ..Default::default()
662            }));
663        } else if let Some(n) = &use_.config {
664            let (source, source_dictionary) = extract_use_source_deprecated(
665                &options,
666                use_,
667                all_capability_names,
668                all_children,
669                None,
670            )?;
671            let target = match &use_.key {
672                None => return Err(Error::validate("\"use config\" must have \"key\" field set.")),
673                Some(t) => t.clone(),
674            };
675            let availability = extract_use_availability_deprecated(use_)?;
676            let type_ = validate::use_config_to_value_type(use_)?;
677
678            let default = if let Some(default) = &use_.config_default {
679                let value = config_value_file::field::config_value_from_json_value(
680                    default,
681                    &type_.clone().into(),
682                )
683                .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", n, e)))?;
684                Some(value.native_into_fidl())
685            } else {
686                None
687            };
688
689            out_uses.push(fdecl::Use::Config(fdecl::UseConfiguration {
690                source: Some(source),
691                source_name: Some(n.clone().into()),
692                target_name: Some(target.into()),
693                availability: Some(availability),
694                type_: Some(translate_value_type(&type_).0),
695                default,
696                source_dictionary,
697                ..Default::default()
698            }));
699        } else if let Some(n) = &use_.dictionary {
700            let (source, source_dictionary) = extract_use_source_deprecated(
701                options,
702                use_,
703                all_capability_names,
704                all_children,
705                None,
706            )?;
707            let availability = extract_use_availability_deprecated(use_)?;
708            for source_name in n.into_iter() {
709                out_uses.push(fdecl::Use::Dictionary(fdecl::UseDictionary {
710                    source: Some(source.clone()),
711                    source_name: Some(source_name.to_string()),
712                    source_dictionary: source_dictionary.clone(),
713                    target_path: Some(
714                        use_.path().as_ref().expect("no path on use dictionary").to_string(),
715                    ),
716                    dependency_type: Some(
717                        use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
718                    ),
719                    availability: Some(availability),
720                    ..Default::default()
721                }));
722            }
723        } else {
724            return Err(Error::internal(format!("no capability in use declaration")));
725        };
726    }
727    Ok(out_uses)
728}
729
730/// `use` rules consume a single capability from one source (parent|framework).
731fn translate_use(
732    options: &CompileOptions<'_>,
733    use_in: &Vec<ContextSpanned<ContextUse>>,
734    all_capability_names: &BTreeSet<Name>,
735    all_children: &BTreeSet<Name>,
736    all_collections: &BTreeSet<Name>,
737) -> Result<Vec<fdecl::Use>, Error> {
738    let mut out_uses = vec![];
739    for spanned_use in use_in {
740        let use_ = &spanned_use.value;
741        if let Some(spanned) = use_.service() {
742            let n = spanned.value;
743            let (source, source_dictionary) = extract_use_source(
744                options,
745                use_,
746                all_capability_names,
747                all_children,
748                Some(all_collections),
749            )?;
750            let target_paths =
751                all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
752            let source_names = n.into_iter();
753            let availability = extract_use_availability(use_)?;
754            for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
755            {
756                out_uses.push(fdecl::Use::Service(fdecl::UseService {
757                    source: Some(source.clone()),
758                    source_name: Some(source_name.to_string()),
759                    source_dictionary: source_dictionary.clone(),
760                    target_path: Some(target_path.to_string()),
761                    dependency_type: Some(
762                        use_.dependency
763                            .clone()
764                            .map(|s| s.value)
765                            .unwrap_or(cm::DependencyType::Strong)
766                            .into(),
767                    ),
768                    availability: Some(availability),
769                    ..Default::default()
770                }));
771            }
772        } else if let Some(spanned) = use_.protocol() {
773            let n = spanned.value;
774            let (source, source_dictionary) =
775                extract_use_source(options, use_, all_capability_names, all_children, None)?;
776            let availability = extract_use_availability(use_)?;
777            if use_.numbered_handle.is_some() {
778                let OneOrMany::One(source_name) = n else {
779                    panic!("numbered_handle: multiple source_name");
780                };
781                out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
782                    source: Some(source.clone()),
783                    source_name: Some(source_name.to_string()),
784                    source_dictionary: source_dictionary.clone(),
785                    target_path: None,
786                    numbered_handle: use_.numbered_handle.as_ref().map(|s| s.value.into()),
787                    dependency_type: Some(
788                        use_.dependency
789                            .clone()
790                            .map(|s| s.value)
791                            .unwrap_or(cm::DependencyType::Strong)
792                            .into(),
793                    ),
794                    availability: Some(availability),
795                    ..Default::default()
796                }));
797                continue;
798            }
799            let source_names = n.into_iter();
800            let target_paths =
801                all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
802            for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
803            {
804                out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
805                    source: Some(source.clone()),
806                    source_name: Some(source_name.to_string()),
807                    source_dictionary: source_dictionary.clone(),
808                    target_path: Some(target_path.into()),
809                    numbered_handle: use_.numbered_handle.as_ref().map(|s| s.value.into()),
810                    dependency_type: Some(
811                        use_.dependency
812                            .clone()
813                            .map(|s| s.value)
814                            .unwrap_or(cm::DependencyType::Strong)
815                            .into(),
816                    ),
817                    availability: Some(availability),
818                    ..Default::default()
819                }));
820            }
821        } else if let Some(spanned) = &use_.directory {
822            let n = &spanned.value;
823            let (source, source_dictionary) =
824                extract_use_source(options, use_, all_capability_names, all_children, None)?;
825            let target_path = one_target_use_path(use_, use_)?;
826            let rights = extract_required_rights(use_, "use")?;
827            let subdir = extract_use_subdir(use_);
828            let availability = extract_use_availability(use_)?;
829            out_uses.push(fdecl::Use::Directory(fdecl::UseDirectory {
830                source: Some(source),
831                source_name: Some(n.clone().into()),
832                source_dictionary,
833                target_path: Some(target_path.into()),
834                rights: Some(rights),
835                subdir: subdir.map(|s| s.into()),
836                dependency_type: Some(
837                    use_.dependency
838                        .clone()
839                        .map(|s| s.value)
840                        .unwrap_or(cm::DependencyType::Strong)
841                        .into(),
842                ),
843                availability: Some(availability),
844                ..Default::default()
845            }));
846        } else if let Some(spanned) = &use_.storage {
847            let n = &spanned.value;
848            let target_path = one_target_use_path(use_, use_)?;
849            let availability = extract_use_availability(use_)?;
850            out_uses.push(fdecl::Use::Storage(fdecl::UseStorage {
851                source_name: Some(n.clone().into()),
852                target_path: Some(target_path.into()),
853                availability: Some(availability),
854                ..Default::default()
855            }));
856        } else if let Some(spanned_names) = &use_.event_stream {
857            let names = &spanned_names.value;
858            let source_names: Vec<String> =
859                annotate_type::<Vec<cm_types::Name>>(names.clone().into())
860                    .iter()
861                    .map(|name| name.to_string())
862                    .collect();
863            let availability = extract_use_availability(use_)?;
864            for name in source_names {
865                let scopes = match use_.scope.clone() {
866                    Some(v) => Some(annotate_type::<Vec<EventScope>>(v.value.into())),
867                    None => None,
868                };
869                let internal_error = format!(
870                    "Internal error in all_target_use_paths when translating an EventStream. \
871                    Please file a bug."
872                );
873                let (source, _source_dictionary) =
874                    extract_use_source(options, use_, all_capability_names, all_children, None)?;
875                out_uses.push(fdecl::Use::EventStream(fdecl::UseEventStream {
876                    source_name: Some(name),
877                    scope: match scopes {
878                        Some(values) => {
879                            let mut output = vec![];
880                            for value in &values {
881                                if let Some(target) = translate_target_ref(
882                                    options,
883                                    value.into(),
884                                    &all_children,
885                                    &all_collections,
886                                    &BTreeSet::new(),
887                                    Some(&TargetAvailability::Required),
888                                )? {
889                                    output.push(target);
890                                }
891                            }
892                            Some(output)
893                        }
894                        None => None,
895                    },
896                    source: Some(source),
897                    target_path: Some(
898                        annotate_type::<Vec<cm_types::Path>>(
899                            all_target_use_paths(use_, use_)
900                                .ok_or_else(|| Error::internal(internal_error.clone()))?
901                                .into(),
902                        )
903                        .iter()
904                        .next()
905                        .ok_or_else(|| Error::internal(internal_error.clone()))?
906                        .to_string(),
907                    ),
908                    filter: match use_.filter.clone() {
909                        Some(dict) => Some(dictionary_from_map(dict.value)?),
910                        None => None,
911                    },
912                    availability: Some(availability),
913                    ..Default::default()
914                }));
915            }
916        } else if let Some(spanned) = &use_.runner {
917            let n = &spanned.value;
918            let (source, source_dictionary) =
919                extract_use_source(&options, use_, all_capability_names, all_children, None)?;
920            #[cfg(fuchsia_api_level_at_least = "HEAD")]
921            out_uses.push(fdecl::Use::Runner(fdecl::UseRunner {
922                source: Some(source),
923                source_name: Some(n.clone().into()),
924                source_dictionary,
925                ..Default::default()
926            }));
927        } else if let Some(spanned) = &use_.config {
928            let n = &spanned.value;
929            let (source, source_dictionary) =
930                extract_use_source(&options, use_, all_capability_names, all_children, None)?;
931            let target = match &use_.key {
932                None => {
933                    return Err(Error::validate_context(
934                        "\"use config\" must have \"key\" field set.",
935                        Some(spanned.origin.clone()),
936                    ));
937                }
938                Some(t) => t.clone(),
939            };
940            let availability = extract_use_availability(use_)?;
941            let type_ = validate::use_config_to_value_type_context(use_)?;
942
943            let default = if let Some(default) = &use_.config_default {
944                let value = config_value_file::field::config_value_from_json_value(
945                    &default.value,
946                    &type_.clone().into(),
947                )
948                .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", n, e)))?;
949                Some(value.native_into_fidl())
950            } else {
951                None
952            };
953
954            out_uses.push(fdecl::Use::Config(fdecl::UseConfiguration {
955                source: Some(source),
956                source_name: Some(n.clone().into()),
957                target_name: Some(target.value.into()),
958                availability: Some(availability),
959                type_: Some(translate_value_type(&type_).0),
960                default,
961                source_dictionary,
962                ..Default::default()
963            }));
964        } else if let Some(n) = &use_.dictionary {
965            let (source, source_dictionary) =
966                extract_use_source(options, use_, all_capability_names, all_children, None)?;
967            let availability = extract_use_availability(use_)?;
968            for source_name in n.value.clone().into_iter() {
969                out_uses.push(fdecl::Use::Dictionary(fdecl::UseDictionary {
970                    source: Some(source.clone()),
971                    source_name: Some(source_name.to_string()),
972                    source_dictionary: source_dictionary.clone(),
973                    target_path: Some(
974                        use_.path().as_ref().expect("no path on use dictionary").value.to_string(),
975                    ),
976                    dependency_type: Some(
977                        use_.dependency
978                            .clone()
979                            .map(|s| s.value)
980                            .unwrap_or(cm::DependencyType::Strong)
981                            .into(),
982                    ),
983                    availability: Some(availability),
984                    ..Default::default()
985                }));
986            }
987        } else {
988            return Err(Error::internal(format!("no capability in use declaration")));
989        };
990    }
991    Ok(out_uses)
992}
993
994/// `expose` rules route a single capability from one or more sources (self|framework|#<child>) to
995/// one or more targets (parent|framework).
996fn translate_expose_deprecated(
997    options: &CompileOptions<'_>,
998    expose_in: &Vec<Expose>,
999    all_capability_names: &BTreeSet<&BorrowedName>,
1000    all_collections: &BTreeSet<&BorrowedName>,
1001    all_children: &BTreeSet<&BorrowedName>,
1002) -> Result<Vec<fdecl::Expose>, Error> {
1003    let mut out_exposes = vec![];
1004    for expose in expose_in.iter() {
1005        let target = extract_expose_target_deprecated(expose);
1006        if let Some(source_names) = expose.service() {
1007            // When there are many `sources` exposed under the same `target_name`, aggregation
1008            // will happen during routing.
1009            let sources =
1010                extract_all_expose_sources_deprecated(options, expose, Some(all_collections));
1011            let target_names = all_target_capability_names_deprecated(expose, expose)
1012                .ok_or_else(|| Error::internal("no capability"))?;
1013            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1014            {
1015                for (source, source_dictionary) in &sources {
1016                    let DerivedSourceInfo { source, source_dictionary, availability } =
1017                        derive_source_and_availability_deprecated(
1018                            expose.availability.as_ref(),
1019                            source.clone(),
1020                            source_dictionary.clone(),
1021                            expose.source_availability.as_ref(),
1022                            all_capability_names,
1023                            all_children,
1024                            all_collections,
1025                        );
1026                    out_exposes.push(fdecl::Expose::Service(fdecl::ExposeService {
1027                        source: Some(source),
1028                        source_name: Some(source_name.to_string()),
1029                        source_dictionary,
1030                        target_name: Some(target_name.to_string()),
1031                        target: Some(target.clone()),
1032                        availability: Some(availability),
1033                        ..Default::default()
1034                    }))
1035                }
1036            }
1037        } else if let Some(n) = expose.protocol() {
1038            let (source, source_dictionary) = extract_single_expose_source_deprecated(
1039                options,
1040                expose,
1041                Some(all_capability_names),
1042            )?;
1043            let source_names = n.into_iter();
1044            let target_names = all_target_capability_names_deprecated(expose, expose)
1045                .ok_or_else(|| Error::internal("no capability"))?;
1046            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1047            {
1048                let DerivedSourceInfo { source, source_dictionary, availability } =
1049                    derive_source_and_availability_deprecated(
1050                        expose.availability.as_ref(),
1051                        source.clone(),
1052                        source_dictionary.clone(),
1053                        expose.source_availability.as_ref(),
1054                        all_capability_names,
1055                        all_children,
1056                        all_collections,
1057                    );
1058                out_exposes.push(fdecl::Expose::Protocol(fdecl::ExposeProtocol {
1059                    source: Some(source),
1060                    source_name: Some(source_name.to_string()),
1061                    source_dictionary,
1062                    target_name: Some(target_name.to_string()),
1063                    target: Some(target.clone()),
1064                    availability: Some(availability),
1065                    ..Default::default()
1066                }))
1067            }
1068        } else if let Some(n) = expose.directory() {
1069            let (source, source_dictionary) =
1070                extract_single_expose_source_deprecated(options, expose, None)?;
1071            let source_names = n.into_iter();
1072            let target_names = all_target_capability_names_deprecated(expose, expose)
1073                .ok_or_else(|| Error::internal("no capability"))?;
1074            let rights = extract_expose_rights_deprecated(expose)?;
1075            let subdir = extract_expose_subdir_deprecated(expose);
1076            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1077            {
1078                let DerivedSourceInfo { source, source_dictionary, availability } =
1079                    derive_source_and_availability_deprecated(
1080                        expose.availability.as_ref(),
1081                        source.clone(),
1082                        source_dictionary.clone(),
1083                        expose.source_availability.as_ref(),
1084                        all_capability_names,
1085                        all_children,
1086                        all_collections,
1087                    );
1088                out_exposes.push(fdecl::Expose::Directory(fdecl::ExposeDirectory {
1089                    source: Some(source),
1090                    source_name: Some(source_name.to_string()),
1091                    source_dictionary,
1092                    target_name: Some(target_name.to_string()),
1093                    target: Some(target.clone()),
1094                    rights,
1095                    subdir: subdir.as_ref().map(|s| s.clone().into()),
1096                    availability: Some(availability),
1097                    ..Default::default()
1098                }))
1099            }
1100        } else if let Some(n) = expose.runner() {
1101            let (source, source_dictionary) =
1102                extract_single_expose_source_deprecated(options, expose, None)?;
1103            let source_names = n.into_iter();
1104            let target_names = all_target_capability_names_deprecated(expose, expose)
1105                .ok_or_else(|| Error::internal("no capability"))?;
1106            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1107            {
1108                out_exposes.push(fdecl::Expose::Runner(fdecl::ExposeRunner {
1109                    source: Some(source.clone()),
1110                    source_name: Some(source_name.to_string()),
1111                    source_dictionary: source_dictionary.clone(),
1112                    target: Some(target.clone()),
1113                    target_name: Some(target_name.to_string()),
1114                    ..Default::default()
1115                }))
1116            }
1117        } else if let Some(n) = expose.resolver() {
1118            let (source, source_dictionary) =
1119                extract_single_expose_source_deprecated(options, expose, None)?;
1120            let source_names = n.into_iter();
1121            let target_names = all_target_capability_names_deprecated(expose, expose)
1122                .ok_or_else(|| Error::internal("no capability"))?;
1123            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1124            {
1125                out_exposes.push(fdecl::Expose::Resolver(fdecl::ExposeResolver {
1126                    source: Some(source.clone()),
1127                    source_name: Some(source_name.to_string()),
1128                    source_dictionary: source_dictionary.clone(),
1129                    target: Some(target.clone()),
1130                    target_name: Some(target_name.to_string()),
1131                    ..Default::default()
1132                }))
1133            }
1134        } else if let Some(n) = expose.dictionary() {
1135            let (source, source_dictionary) =
1136                extract_single_expose_source_deprecated(options, expose, None)?;
1137            let source_names = n.into_iter();
1138            let target_names = all_target_capability_names_deprecated(expose, expose)
1139                .ok_or_else(|| Error::internal("no capability"))?;
1140            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1141            {
1142                let DerivedSourceInfo { source, source_dictionary, availability } =
1143                    derive_source_and_availability_deprecated(
1144                        expose.availability.as_ref(),
1145                        source.clone(),
1146                        source_dictionary.clone(),
1147                        expose.source_availability.as_ref(),
1148                        all_capability_names,
1149                        all_children,
1150                        all_collections,
1151                    );
1152                out_exposes.push(fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
1153                    source: Some(source),
1154                    source_name: Some(source_name.to_string()),
1155                    source_dictionary,
1156                    target_name: Some(target_name.to_string()),
1157                    target: Some(target.clone()),
1158                    availability: Some(availability),
1159                    ..Default::default()
1160                }))
1161            }
1162        } else if let Some(n) = expose.config() {
1163            let (source, source_dictionary) =
1164                extract_single_expose_source_deprecated(options, expose, None)?;
1165            let source_names = n.into_iter();
1166            let target_names = all_target_capability_names_deprecated(expose, expose)
1167                .ok_or_else(|| Error::internal("no capability"))?;
1168            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1169            {
1170                let DerivedSourceInfo { source, source_dictionary, availability } =
1171                    derive_source_and_availability_deprecated(
1172                        expose.availability.as_ref(),
1173                        source.clone(),
1174                        source_dictionary.clone(),
1175                        expose.source_availability.as_ref(),
1176                        all_capability_names,
1177                        all_children,
1178                        all_collections,
1179                    );
1180                out_exposes.push(fdecl::Expose::Config(fdecl::ExposeConfiguration {
1181                    source: Some(source.clone()),
1182                    source_name: Some(source_name.to_string()),
1183                    source_dictionary,
1184                    target: Some(target.clone()),
1185                    target_name: Some(target_name.to_string()),
1186                    availability: Some(availability),
1187                    ..Default::default()
1188                }))
1189            }
1190        } else {
1191            return Err(Error::internal(format!("expose: must specify a known capability")));
1192        }
1193    }
1194    Ok(out_exposes)
1195}
1196
1197/// `expose` rules route a single capability from one or more sources (self|framework|#<child>) to
1198/// one or more targets (parent|framework).
1199fn translate_expose(
1200    options: &CompileOptions<'_>,
1201    expose_in: &Vec<ContextSpanned<ContextExpose>>,
1202    all_capability_names: &BTreeSet<Name>,
1203    all_collections: &BTreeSet<Name>,
1204    all_children: &BTreeSet<Name>,
1205) -> Result<Vec<fdecl::Expose>, Error> {
1206    let mut out_exposes = vec![];
1207    for spanned_expose in expose_in.iter() {
1208        let expose = &spanned_expose.value;
1209        let target = extract_expose_target(expose);
1210        if let Some(source_names) = expose.service() {
1211            // When there are many `sources` exposed under the same `target_name`, aggregation
1212            // will happen during routing.
1213            let sources = extract_all_expose_sources(options, expose, Some(all_collections));
1214            let target_names = all_target_capability_names(expose, expose)
1215                .ok_or_else(|| Error::internal("no capability"))?;
1216            for (source_name, target_name) in
1217                source_names.value.into_iter().zip(target_names.into_iter())
1218            {
1219                for (source, source_dictionary) in &sources {
1220                    let DerivedSourceInfo { source, source_dictionary, availability } =
1221                        derive_source_and_availability(
1222                            expose.availability.as_ref(),
1223                            source.clone(),
1224                            source_dictionary.clone(),
1225                            expose.source_availability.as_ref(),
1226                            all_capability_names,
1227                            all_children,
1228                            all_collections,
1229                        );
1230                    out_exposes.push(fdecl::Expose::Service(fdecl::ExposeService {
1231                        source: Some(source),
1232                        source_name: Some(source_name.to_string()),
1233                        source_dictionary,
1234                        target_name: Some(target_name.to_string()),
1235                        target: Some(target.clone()),
1236                        availability: Some(availability),
1237                        ..Default::default()
1238                    }))
1239                }
1240            }
1241        } else if let Some(n) = expose.protocol() {
1242            let (source, source_dictionary) =
1243                extract_single_expose_source(options, expose, Some(all_capability_names))?;
1244            let source_names = n.value.into_iter();
1245            let target_names = all_target_capability_names(expose, expose)
1246                .ok_or_else(|| Error::internal("no capability"))?;
1247            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1248            {
1249                let DerivedSourceInfo { source, source_dictionary, availability } =
1250                    derive_source_and_availability(
1251                        expose.availability.as_ref(),
1252                        source.clone(),
1253                        source_dictionary.clone(),
1254                        expose.source_availability.as_ref(),
1255                        all_capability_names,
1256                        all_children,
1257                        all_collections,
1258                    );
1259                out_exposes.push(fdecl::Expose::Protocol(fdecl::ExposeProtocol {
1260                    source: Some(source),
1261                    source_name: Some(source_name.to_string()),
1262                    source_dictionary,
1263                    target_name: Some(target_name.to_string()),
1264                    target: Some(target.clone()),
1265                    availability: Some(availability),
1266                    ..Default::default()
1267                }))
1268            }
1269        } else if let Some(n) = expose.directory() {
1270            let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1271            let source_names = n.value.into_iter();
1272            let target_names = all_target_capability_names(expose, expose)
1273                .ok_or_else(|| Error::internal("no capability"))?;
1274            let rights = extract_expose_rights(expose)?;
1275            let subdir = extract_expose_subdir(expose);
1276            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1277            {
1278                let DerivedSourceInfo { source, source_dictionary, availability } =
1279                    derive_source_and_availability(
1280                        expose.availability.as_ref(),
1281                        source.clone(),
1282                        source_dictionary.clone(),
1283                        expose.source_availability.as_ref(),
1284                        all_capability_names,
1285                        all_children,
1286                        all_collections,
1287                    );
1288                out_exposes.push(fdecl::Expose::Directory(fdecl::ExposeDirectory {
1289                    source: Some(source),
1290                    source_name: Some(source_name.to_string()),
1291                    source_dictionary,
1292                    target_name: Some(target_name.to_string()),
1293                    target: Some(target.clone()),
1294                    rights,
1295                    subdir: subdir.as_ref().map(|s| s.clone().into()),
1296                    availability: Some(availability),
1297                    ..Default::default()
1298                }))
1299            }
1300        } else if let Some(n) = expose.runner() {
1301            let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1302            let source_names = n.value.into_iter();
1303            let target_names = all_target_capability_names(expose, expose)
1304                .ok_or_else(|| Error::internal("no capability"))?;
1305            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1306            {
1307                out_exposes.push(fdecl::Expose::Runner(fdecl::ExposeRunner {
1308                    source: Some(source.clone()),
1309                    source_name: Some(source_name.to_string()),
1310                    source_dictionary: source_dictionary.clone(),
1311                    target: Some(target.clone()),
1312                    target_name: Some(target_name.to_string()),
1313                    ..Default::default()
1314                }))
1315            }
1316        } else if let Some(n) = expose.resolver() {
1317            let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1318            let source_names = n.value.into_iter();
1319            let target_names = all_target_capability_names(expose, expose)
1320                .ok_or_else(|| Error::internal("no capability"))?;
1321            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1322            {
1323                out_exposes.push(fdecl::Expose::Resolver(fdecl::ExposeResolver {
1324                    source: Some(source.clone()),
1325                    source_name: Some(source_name.to_string()),
1326                    source_dictionary: source_dictionary.clone(),
1327                    target: Some(target.clone()),
1328                    target_name: Some(target_name.to_string()),
1329                    ..Default::default()
1330                }))
1331            }
1332        } else if let Some(n) = expose.dictionary() {
1333            let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1334            let source_names = n.value.into_iter();
1335            let target_names = all_target_capability_names(expose, expose)
1336                .ok_or_else(|| Error::internal("no capability"))?;
1337            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1338            {
1339                let DerivedSourceInfo { source, source_dictionary, availability } =
1340                    derive_source_and_availability(
1341                        expose.availability.as_ref(),
1342                        source.clone(),
1343                        source_dictionary.clone(),
1344                        expose.source_availability.as_ref(),
1345                        all_capability_names,
1346                        all_children,
1347                        all_collections,
1348                    );
1349                out_exposes.push(fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
1350                    source: Some(source),
1351                    source_name: Some(source_name.to_string()),
1352                    source_dictionary,
1353                    target_name: Some(target_name.to_string()),
1354                    target: Some(target.clone()),
1355                    availability: Some(availability),
1356                    ..Default::default()
1357                }))
1358            }
1359        } else if let Some(n) = expose.config() {
1360            let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1361            let source_names = n.value.into_iter();
1362            let target_names = all_target_capability_names(expose, expose)
1363                .ok_or_else(|| Error::internal("no capability"))?;
1364            for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1365            {
1366                let DerivedSourceInfo { source, source_dictionary, availability } =
1367                    derive_source_and_availability(
1368                        expose.availability.as_ref(),
1369                        source.clone(),
1370                        source_dictionary.clone(),
1371                        expose.source_availability.as_ref(),
1372                        all_capability_names,
1373                        all_children,
1374                        all_collections,
1375                    );
1376                out_exposes.push(fdecl::Expose::Config(fdecl::ExposeConfiguration {
1377                    source: Some(source.clone()),
1378                    source_name: Some(source_name.to_string()),
1379                    source_dictionary,
1380                    target: Some(target.clone()),
1381                    target_name: Some(target_name.to_string()),
1382                    availability: Some(availability),
1383                    ..Default::default()
1384                }))
1385            }
1386        } else {
1387            return Err(Error::internal(format!("expose: must specify a known capability")));
1388        }
1389    }
1390    Ok(out_exposes)
1391}
1392
1393impl<T> Into<Vec<T>> for OneOrMany<T> {
1394    fn into(self) -> Vec<T> {
1395        match self {
1396            OneOrMany::One(one) => vec![one],
1397            OneOrMany::Many(many) => many,
1398        }
1399    }
1400}
1401
1402/// Allows the above Into to work by annotating the type.
1403fn annotate_type<T>(val: T) -> T {
1404    val
1405}
1406
1407struct DerivedSourceInfo {
1408    source: fdecl::Ref,
1409    source_dictionary: Option<String>,
1410    availability: fdecl::Availability,
1411}
1412
1413/// If the `source` is not found and `source_availability` is `Unknown`, returns a `Void` source.
1414/// Otherwise, returns the source unchanged.
1415fn derive_source_and_availability_deprecated(
1416    availability: Option<&Availability>,
1417    source: fdecl::Ref,
1418    source_dictionary: Option<String>,
1419    source_availability: Option<&SourceAvailability>,
1420    all_capability_names: &BTreeSet<&BorrowedName>,
1421    all_children: &BTreeSet<&BorrowedName>,
1422    all_collections: &BTreeSet<&BorrowedName>,
1423) -> DerivedSourceInfo {
1424    let availability = availability.map(|a| match a {
1425        Availability::Required => fdecl::Availability::Required,
1426        Availability::Optional => fdecl::Availability::Optional,
1427        Availability::SameAsTarget => fdecl::Availability::SameAsTarget,
1428        Availability::Transitional => fdecl::Availability::Transitional,
1429    });
1430    if source_availability != Some(&SourceAvailability::Unknown) {
1431        return DerivedSourceInfo {
1432            source,
1433            source_dictionary,
1434            availability: availability.unwrap_or(fdecl::Availability::Required),
1435        };
1436    }
1437    match &source {
1438        fdecl::Ref::Child(fdecl::ChildRef { name, .. })
1439            if !all_children.contains(name.as_str()) =>
1440        {
1441            DerivedSourceInfo {
1442                source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1443                source_dictionary: None,
1444                availability: availability.unwrap_or(fdecl::Availability::Optional),
1445            }
1446        }
1447        fdecl::Ref::Collection(fdecl::CollectionRef { name, .. })
1448            if !all_collections.contains(name.as_str()) =>
1449        {
1450            DerivedSourceInfo {
1451                source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1452                source_dictionary: None,
1453                availability: availability.unwrap_or(fdecl::Availability::Optional),
1454            }
1455        }
1456        fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. })
1457            if !all_capability_names.contains(name.as_str()) =>
1458        {
1459            DerivedSourceInfo {
1460                source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1461                source_dictionary: None,
1462                availability: availability.unwrap_or(fdecl::Availability::Optional),
1463            }
1464        }
1465        _ => DerivedSourceInfo {
1466            source,
1467            source_dictionary,
1468            availability: availability.unwrap_or(fdecl::Availability::Required),
1469        },
1470    }
1471}
1472
1473/// If the `source` is not found and `source_availability` is `Unknown`, returns a `Void` source.
1474/// Otherwise, returns the source unchanged.
1475fn derive_source_and_availability(
1476    availability: Option<&ContextSpanned<Availability>>,
1477    source: fdecl::Ref,
1478    source_dictionary: Option<String>,
1479    source_availability: Option<&ContextSpanned<SourceAvailability>>,
1480    all_capability_names: &BTreeSet<Name>,
1481    all_children: &BTreeSet<Name>,
1482    all_collections: &BTreeSet<Name>,
1483) -> DerivedSourceInfo {
1484    let availability = availability.map(|a| match a.value {
1485        Availability::Required => fdecl::Availability::Required,
1486        Availability::Optional => fdecl::Availability::Optional,
1487        Availability::SameAsTarget => fdecl::Availability::SameAsTarget,
1488        Availability::Transitional => fdecl::Availability::Transitional,
1489    });
1490    if source_availability.as_ref().map(|s| s.value.clone()) != Some(SourceAvailability::Unknown) {
1491        return DerivedSourceInfo {
1492            source,
1493            source_dictionary,
1494            availability: availability.unwrap_or(fdecl::Availability::Required),
1495        };
1496    }
1497    match &source {
1498        fdecl::Ref::Child(fdecl::ChildRef { name, .. })
1499            if !all_children.contains(name.as_str()) =>
1500        {
1501            DerivedSourceInfo {
1502                source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1503                source_dictionary: None,
1504                availability: availability.unwrap_or(fdecl::Availability::Optional),
1505            }
1506        }
1507        fdecl::Ref::Collection(fdecl::CollectionRef { name, .. })
1508            if !all_collections.contains(name.as_str()) =>
1509        {
1510            DerivedSourceInfo {
1511                source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1512                source_dictionary: None,
1513                availability: availability.unwrap_or(fdecl::Availability::Optional),
1514            }
1515        }
1516        fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. })
1517            if !all_capability_names.contains(name.as_str()) =>
1518        {
1519            DerivedSourceInfo {
1520                source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1521                source_dictionary: None,
1522                availability: availability.unwrap_or(fdecl::Availability::Optional),
1523            }
1524        }
1525        _ => DerivedSourceInfo {
1526            source,
1527            source_dictionary,
1528            availability: availability.unwrap_or(fdecl::Availability::Required),
1529        },
1530    }
1531}
1532
1533/// Emit a set of direct offers from `offer_to_all` for `target`, unless it
1534/// overlaps with an offer in `direct_offers`.
1535fn maybe_generate_direct_offer_from_all_deprecated(
1536    offer_to_all: &Offer,
1537    direct_offers: &[Offer],
1538    target: &BorrowedName,
1539) -> Vec<Offer> {
1540    assert!(offer_to_all.protocol.is_some() || offer_to_all.dictionary.is_some());
1541    let mut returned_offers = vec![];
1542    for mut local_offer in offer_to_all
1543        .protocol
1544        .as_ref()
1545        .unwrap_or(&OneOrMany::Many(vec![]))
1546        .iter()
1547        .map(|individual_protocol| {
1548            let mut local_offer = offer_to_all.clone();
1549            local_offer.protocol = Some(OneOrMany::One(individual_protocol.clone()));
1550            local_offer
1551        })
1552        .chain(
1553            offer_to_all.dictionary.as_ref().unwrap_or(&OneOrMany::Many(vec![])).into_iter().map(
1554                |dictionary| {
1555                    let mut local_offer = offer_to_all.clone();
1556                    local_offer.dictionary = Some(OneOrMany::One(dictionary.clone()));
1557                    local_offer
1558                },
1559            ),
1560        )
1561    {
1562        let disallowed_offer_source = OfferFromRef::Named(target.into());
1563        if direct_offers.iter().all(|direct| {
1564            // Assume that the cml being parsed is valid, which is the only
1565            // way that this function errors
1566            !offer_to_all_would_duplicate(&local_offer, direct, target).unwrap()
1567        }) && !local_offer.from.iter().any(|from| from == &disallowed_offer_source)
1568        {
1569            local_offer.to = OneOrMany::One(OfferToRef::Named(target.into()));
1570            returned_offers.push(local_offer);
1571        }
1572    }
1573
1574    returned_offers
1575}
1576
1577fn maybe_generate_direct_offer_from_all(
1578    offer_to_all: &ContextSpanned<ContextOffer>,
1579    direct_offers: &[ContextSpanned<ContextOffer>],
1580    target: &BorrowedName,
1581) -> Vec<ContextSpanned<ContextOffer>> {
1582    assert!(offer_to_all.value.protocol.is_some() || offer_to_all.value.dictionary.is_some());
1583    let mut returned_offers = vec![];
1584
1585    let protocol_iter = offer_to_all.value.protocol.as_ref().into_iter().flat_map(|spanned| {
1586        let origin = spanned.origin.clone();
1587        spanned.value.iter().map(move |individual_protocol| {
1588            let mut local_offer_spanned = offer_to_all.clone();
1589
1590            local_offer_spanned.value.protocol = Some(ContextSpanned {
1591                value: OneOrMany::One(individual_protocol.clone()),
1592                origin: origin.clone(),
1593            });
1594            local_offer_spanned
1595        })
1596    });
1597
1598    let dict_iter = offer_to_all.value.dictionary.as_ref().into_iter().flat_map(|spanned| {
1599        let origin = spanned.origin.clone();
1600        spanned.value.iter().map(move |dictionary| {
1601            let mut local_offer_spanned = offer_to_all.clone();
1602
1603            local_offer_spanned.value.dictionary = Some(ContextSpanned {
1604                value: OneOrMany::One(dictionary.clone()),
1605                origin: origin.clone(),
1606            });
1607            local_offer_spanned
1608        })
1609    });
1610
1611    for mut local_offer_spanned in protocol_iter.chain(dict_iter) {
1612        let disallowed_offer_source = OfferFromRef::Named(target.into());
1613
1614        if direct_offers.iter().all(|direct| {
1615            !offer_to_all_would_duplicate_context(&local_offer_spanned, direct, target).unwrap()
1616        }) && !local_offer_spanned
1617            .value
1618            .from
1619            .value
1620            .iter()
1621            .any(|from| from == &disallowed_offer_source)
1622        {
1623            local_offer_spanned.value.to = ContextSpanned {
1624                value: OneOrMany::One(OfferToRef::Named(target.into())),
1625                origin: local_offer_spanned.origin.clone(),
1626            };
1627            returned_offers.push(local_offer_spanned);
1628        }
1629    }
1630
1631    returned_offers
1632}
1633
1634fn expand_offer_to_all_deprecated(
1635    offers_in: &Vec<Offer>,
1636    children: &BTreeSet<&BorrowedName>,
1637    collections: &BTreeSet<&BorrowedName>,
1638) -> Vec<Offer> {
1639    let offers_to_all =
1640        offers_in.iter().filter(|offer| matches!(offer.to, OneOrMany::One(OfferToRef::All)));
1641
1642    let mut direct_offers = offers_in
1643        .iter()
1644        .filter(|o| !matches!(o.to, OneOrMany::One(OfferToRef::All)))
1645        .map(Offer::clone)
1646        .collect::<Vec<Offer>>();
1647
1648    for offer_to_all in offers_to_all {
1649        for target in children.iter().chain(collections.iter()) {
1650            let offers = maybe_generate_direct_offer_from_all_deprecated(
1651                offer_to_all,
1652                &direct_offers,
1653                target,
1654            );
1655            for offer in offers {
1656                direct_offers.push(offer);
1657            }
1658        }
1659    }
1660
1661    direct_offers
1662}
1663
1664fn expand_offer_to_all(
1665    offers_in: &Vec<ContextSpanned<ContextOffer>>,
1666    children: &BTreeSet<Name>,
1667    collections: &BTreeSet<Name>,
1668) -> Vec<ContextSpanned<ContextOffer>> {
1669    let offers_to_all = offers_in
1670        .iter()
1671        .filter(|offer| matches!(offer.value.to.value, OneOrMany::One(OfferToRef::All)));
1672
1673    let mut direct_offers = offers_in
1674        .iter()
1675        .filter(|o| !matches!(o.value.to.value, OneOrMany::One(OfferToRef::All)))
1676        .cloned()
1677        .collect::<Vec<ContextSpanned<ContextOffer>>>();
1678
1679    for offer_to_all in offers_to_all {
1680        for target in children.iter().chain(collections.iter()) {
1681            let offers = maybe_generate_direct_offer_from_all(offer_to_all, &direct_offers, target);
1682            for offer in offers {
1683                direct_offers.push(offer);
1684            }
1685        }
1686    }
1687
1688    direct_offers
1689}
1690
1691/// `offer` rules route multiple capabilities from multiple sources to multiple targets.
1692fn translate_offer_deprecated(
1693    options: &CompileOptions<'_>,
1694    offer_in: &Vec<Offer>,
1695    all_capability_names: &BTreeSet<&BorrowedName>,
1696    all_children: &BTreeSet<&BorrowedName>,
1697    all_collections: &BTreeSet<&BorrowedName>,
1698) -> Result<Vec<fdecl::Offer>, Error> {
1699    let mut out_offers = vec![];
1700    let expanded_offers = expand_offer_to_all_deprecated(offer_in, all_children, all_collections);
1701    for offer in &expanded_offers {
1702        if let Some(n) = offer.service() {
1703            let entries = extract_offer_sources_and_targets_deprecated(
1704                options,
1705                offer,
1706                n,
1707                all_capability_names,
1708                all_children,
1709                all_collections,
1710            )?;
1711            for (source, source_dictionary, source_name, target, target_name) in entries {
1712                let DerivedSourceInfo { source, source_dictionary, availability } =
1713                    derive_source_and_availability_deprecated(
1714                        offer.availability.as_ref(),
1715                        source,
1716                        source_dictionary,
1717                        offer.source_availability.as_ref(),
1718                        all_capability_names,
1719                        all_children,
1720                        all_collections,
1721                    );
1722                out_offers.push(fdecl::Offer::Service(fdecl::OfferService {
1723                    source: Some(source),
1724                    source_name: Some(source_name.to_string()),
1725                    source_dictionary,
1726                    target: Some(target),
1727                    target_name: Some(target_name.to_string()),
1728                    availability: Some(availability),
1729                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
1730                    dependency_type: Some(
1731                        offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1732                    ),
1733                    ..Default::default()
1734                }));
1735            }
1736        } else if let Some(n) = offer.protocol() {
1737            let entries = extract_offer_sources_and_targets_deprecated(
1738                options,
1739                offer,
1740                n,
1741                all_capability_names,
1742                all_children,
1743                all_collections,
1744            )?;
1745            for (source, source_dictionary, source_name, target, target_name) in entries {
1746                let DerivedSourceInfo { source, source_dictionary, availability } =
1747                    derive_source_and_availability_deprecated(
1748                        offer.availability.as_ref(),
1749                        source,
1750                        source_dictionary,
1751                        offer.source_availability.as_ref(),
1752                        all_capability_names,
1753                        all_children,
1754                        all_collections,
1755                    );
1756                out_offers.push(fdecl::Offer::Protocol(fdecl::OfferProtocol {
1757                    source: Some(source),
1758                    source_name: Some(source_name.to_string()),
1759                    source_dictionary,
1760                    target: Some(target),
1761                    target_name: Some(target_name.to_string()),
1762                    dependency_type: Some(
1763                        offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1764                    ),
1765                    availability: Some(availability),
1766                    ..Default::default()
1767                }));
1768            }
1769        } else if let Some(n) = offer.directory() {
1770            let entries = extract_offer_sources_and_targets_deprecated(
1771                options,
1772                offer,
1773                n,
1774                all_capability_names,
1775                all_children,
1776                all_collections,
1777            )?;
1778            for (source, source_dictionary, source_name, target, target_name) in entries {
1779                let DerivedSourceInfo { source, source_dictionary, availability } =
1780                    derive_source_and_availability_deprecated(
1781                        offer.availability.as_ref(),
1782                        source,
1783                        source_dictionary,
1784                        offer.source_availability.as_ref(),
1785                        all_capability_names,
1786                        all_children,
1787                        all_collections,
1788                    );
1789                out_offers.push(fdecl::Offer::Directory(fdecl::OfferDirectory {
1790                    source: Some(source),
1791                    source_name: Some(source_name.to_string()),
1792                    source_dictionary,
1793                    target: Some(target),
1794                    target_name: Some(target_name.to_string()),
1795                    rights: extract_offer_rights_deprecated(offer)?,
1796                    subdir: extract_offer_subdir_deprecated(offer).map(|s| s.into()),
1797                    dependency_type: Some(
1798                        offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1799                    ),
1800                    availability: Some(availability),
1801                    ..Default::default()
1802                }));
1803            }
1804        } else if let Some(n) = offer.storage() {
1805            let entries = extract_offer_sources_and_targets_deprecated(
1806                options,
1807                offer,
1808                n,
1809                all_capability_names,
1810                all_children,
1811                all_collections,
1812            )?;
1813            for (source, source_dictionary, source_name, target, target_name) in entries {
1814                let DerivedSourceInfo { source, source_dictionary: _, availability } =
1815                    derive_source_and_availability_deprecated(
1816                        offer.availability.as_ref(),
1817                        source,
1818                        source_dictionary,
1819                        offer.source_availability.as_ref(),
1820                        all_capability_names,
1821                        all_children,
1822                        all_collections,
1823                    );
1824                out_offers.push(fdecl::Offer::Storage(fdecl::OfferStorage {
1825                    source: Some(source),
1826                    source_name: Some(source_name.to_string()),
1827                    target: Some(target),
1828                    target_name: Some(target_name.to_string()),
1829                    availability: Some(availability),
1830                    ..Default::default()
1831                }));
1832            }
1833        } else if let Some(n) = offer.runner() {
1834            let entries = extract_offer_sources_and_targets_deprecated(
1835                options,
1836                offer,
1837                n,
1838                all_capability_names,
1839                all_children,
1840                all_collections,
1841            )?;
1842            for (source, source_dictionary, source_name, target, target_name) in entries {
1843                out_offers.push(fdecl::Offer::Runner(fdecl::OfferRunner {
1844                    source: Some(source),
1845                    source_name: Some(source_name.to_string()),
1846                    source_dictionary,
1847                    target: Some(target),
1848                    target_name: Some(target_name.to_string()),
1849                    ..Default::default()
1850                }));
1851            }
1852        } else if let Some(n) = offer.resolver() {
1853            let entries = extract_offer_sources_and_targets_deprecated(
1854                options,
1855                offer,
1856                n,
1857                all_capability_names,
1858                all_children,
1859                all_collections,
1860            )?;
1861            for (source, source_dictionary, source_name, target, target_name) in entries {
1862                out_offers.push(fdecl::Offer::Resolver(fdecl::OfferResolver {
1863                    source: Some(source),
1864                    source_name: Some(source_name.to_string()),
1865                    source_dictionary,
1866                    target: Some(target),
1867                    target_name: Some(target_name.to_string()),
1868                    ..Default::default()
1869                }));
1870            }
1871        } else if let Some(n) = offer.event_stream() {
1872            let entries = extract_offer_sources_and_targets_deprecated(
1873                options,
1874                offer,
1875                n,
1876                all_capability_names,
1877                all_children,
1878                all_collections,
1879            )?;
1880            for (source, source_dictionary, source_name, target, target_name) in entries {
1881                let DerivedSourceInfo { source, source_dictionary: _, availability } =
1882                    derive_source_and_availability_deprecated(
1883                        offer.availability.as_ref(),
1884                        source,
1885                        source_dictionary,
1886                        offer.source_availability.as_ref(),
1887                        all_capability_names,
1888                        all_children,
1889                        all_collections,
1890                    );
1891                let scopes = match offer.scope.clone() {
1892                    Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
1893                    None => None,
1894                };
1895                out_offers.push(fdecl::Offer::EventStream(fdecl::OfferEventStream {
1896                    source: Some(source),
1897                    source_name: Some(source_name.to_string()),
1898                    target: Some(target),
1899                    target_name: Some(target_name.to_string()),
1900                    scope: match scopes {
1901                        Some(values) => {
1902                            let mut output = vec![];
1903                            for value in &values {
1904                                static EMPTY_SET: BTreeSet<&BorrowedName> = BTreeSet::new();
1905                                if let Some(target) = translate_target_ref_deprecated(
1906                                    options,
1907                                    value.into(),
1908                                    &all_children,
1909                                    &all_collections,
1910                                    &EMPTY_SET,
1911                                    offer.target_availability.as_ref(),
1912                                )? {
1913                                    output.push(target);
1914                                }
1915                            }
1916                            Some(output)
1917                        }
1918                        None => None,
1919                    },
1920                    availability: Some(availability),
1921                    ..Default::default()
1922                }));
1923            }
1924        } else if let Some(n) = offer.dictionary() {
1925            let entries = extract_offer_sources_and_targets_deprecated(
1926                options,
1927                offer,
1928                n,
1929                all_capability_names,
1930                all_children,
1931                all_collections,
1932            )?;
1933            for (source, source_dictionary, source_name, target, target_name) in entries {
1934                let DerivedSourceInfo { source, source_dictionary, availability } =
1935                    derive_source_and_availability_deprecated(
1936                        offer.availability.as_ref(),
1937                        source,
1938                        source_dictionary,
1939                        offer.source_availability.as_ref(),
1940                        all_capability_names,
1941                        all_children,
1942                        all_collections,
1943                    );
1944                out_offers.push(fdecl::Offer::Dictionary(fdecl::OfferDictionary {
1945                    source: Some(source),
1946                    source_name: Some(source_name.to_string()),
1947                    source_dictionary,
1948                    target: Some(target),
1949                    target_name: Some(target_name.to_string()),
1950                    dependency_type: Some(
1951                        offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1952                    ),
1953                    availability: Some(availability),
1954                    ..Default::default()
1955                }));
1956            }
1957        } else if let Some(n) = offer.config() {
1958            let entries = extract_offer_sources_and_targets_deprecated(
1959                options,
1960                offer,
1961                n,
1962                all_capability_names,
1963                all_children,
1964                all_collections,
1965            )?;
1966            for (source, source_dictionary, source_name, target, target_name) in entries {
1967                let DerivedSourceInfo { source, source_dictionary, availability } =
1968                    derive_source_and_availability_deprecated(
1969                        offer.availability.as_ref(),
1970                        source,
1971                        source_dictionary,
1972                        offer.source_availability.as_ref(),
1973                        all_capability_names,
1974                        all_children,
1975                        all_collections,
1976                    );
1977                out_offers.push(fdecl::Offer::Config(fdecl::OfferConfiguration {
1978                    source: Some(source),
1979                    source_name: Some(source_name.to_string()),
1980                    target: Some(target),
1981                    target_name: Some(target_name.to_string()),
1982                    availability: Some(availability),
1983                    source_dictionary,
1984                    ..Default::default()
1985                }));
1986            }
1987        } else {
1988            return Err(Error::internal(format!("no capability")));
1989        }
1990    }
1991    Ok(out_offers)
1992}
1993
1994/// `offer` rules route multiple capabilities from multiple sources to multiple targets.
1995fn translate_offer(
1996    options: &CompileOptions<'_>,
1997    offer_in: &Vec<ContextSpanned<ContextOffer>>,
1998    all_capability_names: &BTreeSet<Name>,
1999    all_children: &BTreeSet<Name>,
2000    all_collections: &BTreeSet<Name>,
2001) -> Result<Vec<fdecl::Offer>, Error> {
2002    let mut out_offers = vec![];
2003    let expanded_offers = expand_offer_to_all(offer_in, all_children, all_collections);
2004    for offer_spanned in &expanded_offers {
2005        let offer = &offer_spanned.value;
2006        if let Some(n) = offer.service() {
2007            let entries = extract_offer_sources_and_targets(
2008                options,
2009                offer,
2010                n.value,
2011                all_capability_names,
2012                all_children,
2013                all_collections,
2014            )?;
2015            for (source, source_dictionary, source_name, target, target_name) in entries {
2016                let DerivedSourceInfo { source, source_dictionary, availability } =
2017                    derive_source_and_availability(
2018                        offer.availability.as_ref(),
2019                        source,
2020                        source_dictionary,
2021                        offer.source_availability.as_ref(),
2022                        all_capability_names,
2023                        all_children,
2024                        all_collections,
2025                    );
2026                out_offers.push(fdecl::Offer::Service(fdecl::OfferService {
2027                    source: Some(source),
2028                    source_name: Some(source_name.to_string()),
2029                    source_dictionary,
2030                    target: Some(target),
2031                    target_name: Some(target_name.to_string()),
2032                    availability: Some(availability),
2033                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
2034                    dependency_type: Some(
2035                        offer
2036                            .dependency
2037                            .clone()
2038                            .map(|s| s.value)
2039                            .unwrap_or(cm::DependencyType::Strong)
2040                            .into(),
2041                    ),
2042                    ..Default::default()
2043                }));
2044            }
2045        } else if let Some(n) = offer.protocol() {
2046            let entries = extract_offer_sources_and_targets(
2047                options,
2048                offer,
2049                n.value,
2050                all_capability_names,
2051                all_children,
2052                all_collections,
2053            )?;
2054            for (source, source_dictionary, source_name, target, target_name) in entries {
2055                let DerivedSourceInfo { source, source_dictionary, availability } =
2056                    derive_source_and_availability(
2057                        offer.availability.as_ref(),
2058                        source,
2059                        source_dictionary,
2060                        offer.source_availability.as_ref(),
2061                        all_capability_names,
2062                        all_children,
2063                        all_collections,
2064                    );
2065                out_offers.push(fdecl::Offer::Protocol(fdecl::OfferProtocol {
2066                    source: Some(source),
2067                    source_name: Some(source_name.to_string()),
2068                    source_dictionary,
2069                    target: Some(target),
2070                    target_name: Some(target_name.to_string()),
2071                    dependency_type: Some(
2072                        offer
2073                            .dependency
2074                            .clone()
2075                            .map(|s| s.value)
2076                            .unwrap_or(cm::DependencyType::Strong)
2077                            .into(),
2078                    ),
2079                    availability: Some(availability),
2080                    ..Default::default()
2081                }));
2082            }
2083        } else if let Some(n) = offer.directory() {
2084            let entries = extract_offer_sources_and_targets(
2085                options,
2086                offer,
2087                n.value,
2088                all_capability_names,
2089                all_children,
2090                all_collections,
2091            )?;
2092            for (source, source_dictionary, source_name, target, target_name) in entries {
2093                let DerivedSourceInfo { source, source_dictionary, availability } =
2094                    derive_source_and_availability(
2095                        offer.availability.as_ref(),
2096                        source,
2097                        source_dictionary,
2098                        offer.source_availability.as_ref(),
2099                        all_capability_names,
2100                        all_children,
2101                        all_collections,
2102                    );
2103                out_offers.push(fdecl::Offer::Directory(fdecl::OfferDirectory {
2104                    source: Some(source),
2105                    source_name: Some(source_name.to_string()),
2106                    source_dictionary,
2107                    target: Some(target),
2108                    target_name: Some(target_name.to_string()),
2109                    rights: extract_offer_rights(&offer)?,
2110                    subdir: extract_offer_subdir(&offer).map(|s| s.into()),
2111                    dependency_type: Some(
2112                        offer
2113                            .dependency
2114                            .clone()
2115                            .map(|s| s.value)
2116                            .unwrap_or(cm::DependencyType::Strong)
2117                            .into(),
2118                    ),
2119                    availability: Some(availability),
2120                    ..Default::default()
2121                }));
2122            }
2123        } else if let Some(n) = offer.storage() {
2124            let entries = extract_offer_sources_and_targets(
2125                options,
2126                offer,
2127                n.value,
2128                all_capability_names,
2129                all_children,
2130                all_collections,
2131            )?;
2132            for (source, source_dictionary, source_name, target, target_name) in entries {
2133                let DerivedSourceInfo { source, source_dictionary: _, availability } =
2134                    derive_source_and_availability(
2135                        offer.availability.as_ref(),
2136                        source,
2137                        source_dictionary,
2138                        offer.source_availability.as_ref(),
2139                        all_capability_names,
2140                        all_children,
2141                        all_collections,
2142                    );
2143                out_offers.push(fdecl::Offer::Storage(fdecl::OfferStorage {
2144                    source: Some(source),
2145                    source_name: Some(source_name.to_string()),
2146                    target: Some(target),
2147                    target_name: Some(target_name.to_string()),
2148                    availability: Some(availability),
2149                    ..Default::default()
2150                }));
2151            }
2152        } else if let Some(n) = offer.runner() {
2153            let entries = extract_offer_sources_and_targets(
2154                options,
2155                offer,
2156                n.value,
2157                all_capability_names,
2158                all_children,
2159                all_collections,
2160            )?;
2161            for (source, source_dictionary, source_name, target, target_name) in entries {
2162                out_offers.push(fdecl::Offer::Runner(fdecl::OfferRunner {
2163                    source: Some(source),
2164                    source_name: Some(source_name.to_string()),
2165                    source_dictionary,
2166                    target: Some(target),
2167                    target_name: Some(target_name.to_string()),
2168                    ..Default::default()
2169                }));
2170            }
2171        } else if let Some(n) = offer.resolver() {
2172            let entries = extract_offer_sources_and_targets(
2173                options,
2174                offer,
2175                n.value,
2176                all_capability_names,
2177                all_children,
2178                all_collections,
2179            )?;
2180            for (source, source_dictionary, source_name, target, target_name) in entries {
2181                out_offers.push(fdecl::Offer::Resolver(fdecl::OfferResolver {
2182                    source: Some(source),
2183                    source_name: Some(source_name.to_string()),
2184                    source_dictionary,
2185                    target: Some(target),
2186                    target_name: Some(target_name.to_string()),
2187                    ..Default::default()
2188                }));
2189            }
2190        } else if let Some(n) = offer.event_stream() {
2191            let entries = extract_offer_sources_and_targets(
2192                options,
2193                offer,
2194                n.value,
2195                all_capability_names,
2196                all_children,
2197                all_collections,
2198            )?;
2199            for (source, source_dictionary, source_name, target, target_name) in entries {
2200                let DerivedSourceInfo { source, source_dictionary: _, availability } =
2201                    derive_source_and_availability(
2202                        offer.availability.as_ref(),
2203                        source,
2204                        source_dictionary,
2205                        offer.source_availability.as_ref(),
2206                        all_capability_names,
2207                        all_children,
2208                        all_collections,
2209                    );
2210                let scopes = match offer.scope.clone() {
2211                    Some(value) => Some(annotate_type::<Vec<EventScope>>(value.value.into())),
2212                    None => None,
2213                };
2214                out_offers.push(fdecl::Offer::EventStream(fdecl::OfferEventStream {
2215                    source: Some(source),
2216                    source_name: Some(source_name.to_string()),
2217                    target: Some(target),
2218                    target_name: Some(target_name.to_string()),
2219                    scope: match scopes {
2220                        Some(values) => {
2221                            let mut output = vec![];
2222                            for value in &values {
2223                                if let Some(target) = translate_target_ref(
2224                                    options,
2225                                    value.into(),
2226                                    &all_children,
2227                                    &all_collections,
2228                                    &BTreeSet::new(),
2229                                    offer.target_availability.clone().map(|s| s.value).as_ref(),
2230                                )? {
2231                                    output.push(target);
2232                                }
2233                            }
2234                            Some(output)
2235                        }
2236                        None => None,
2237                    },
2238                    availability: Some(availability),
2239                    ..Default::default()
2240                }));
2241            }
2242        } else if let Some(n) = offer.dictionary() {
2243            let entries = extract_offer_sources_and_targets(
2244                options,
2245                offer,
2246                n.value,
2247                all_capability_names,
2248                all_children,
2249                all_collections,
2250            )?;
2251            for (source, source_dictionary, source_name, target, target_name) in entries {
2252                let DerivedSourceInfo { source, source_dictionary, availability } =
2253                    derive_source_and_availability(
2254                        offer.availability.as_ref(),
2255                        source,
2256                        source_dictionary,
2257                        offer.source_availability.as_ref(),
2258                        all_capability_names,
2259                        all_children,
2260                        all_collections,
2261                    );
2262                out_offers.push(fdecl::Offer::Dictionary(fdecl::OfferDictionary {
2263                    source: Some(source),
2264                    source_name: Some(source_name.to_string()),
2265                    source_dictionary,
2266                    target: Some(target),
2267                    target_name: Some(target_name.to_string()),
2268                    dependency_type: Some(
2269                        offer
2270                            .dependency
2271                            .clone()
2272                            .map(|s| s.value)
2273                            .unwrap_or(cm::DependencyType::Strong)
2274                            .into(),
2275                    ),
2276                    availability: Some(availability),
2277                    ..Default::default()
2278                }));
2279            }
2280        } else if let Some(n) = offer.config() {
2281            let entries = extract_offer_sources_and_targets(
2282                options,
2283                offer,
2284                n.value,
2285                all_capability_names,
2286                all_children,
2287                all_collections,
2288            )?;
2289            for (source, source_dictionary, source_name, target, target_name) in entries {
2290                let DerivedSourceInfo { source, source_dictionary, availability } =
2291                    derive_source_and_availability(
2292                        offer.availability.as_ref(),
2293                        source,
2294                        source_dictionary,
2295                        offer.source_availability.as_ref(),
2296                        all_capability_names,
2297                        all_children,
2298                        all_collections,
2299                    );
2300                out_offers.push(fdecl::Offer::Config(fdecl::OfferConfiguration {
2301                    source: Some(source),
2302                    source_name: Some(source_name.to_string()),
2303                    target: Some(target),
2304                    target_name: Some(target_name.to_string()),
2305                    availability: Some(availability),
2306                    source_dictionary,
2307                    ..Default::default()
2308                }));
2309            }
2310        } else {
2311            return Err(Error::internal(format!("no capability")));
2312        }
2313    }
2314    Ok(out_offers)
2315}
2316
2317fn translate_children_deprecated(children_in: &Vec<Child>) -> Vec<fdecl::Child> {
2318    let mut out_children = vec![];
2319    for child in children_in.iter() {
2320        out_children.push(fdecl::Child {
2321            name: Some(child.name.clone().into()),
2322            url: Some(child.url.clone().into()),
2323            startup: Some(child.startup.clone().into()),
2324            environment: extract_environment_ref_deprecated(child.environment.as_ref())
2325                .map(|e| e.into()),
2326            on_terminate: child.on_terminate.as_ref().map(|r| r.clone().into()),
2327            ..Default::default()
2328        });
2329    }
2330    out_children
2331}
2332
2333fn translate_children(children_in: &Vec<ContextSpanned<ContextChild>>) -> Vec<fdecl::Child> {
2334    let mut out_children = vec![];
2335    for child_raw in children_in.iter() {
2336        let child = &child_raw.value;
2337        out_children.push(fdecl::Child {
2338            name: Some(child.name.value.clone().into()),
2339            url: Some(child.url.value.clone().into()),
2340            startup: Some(child.startup.value.clone().into()),
2341            environment: extract_environment_ref(child.environment.as_ref()).map(|e| e.into()),
2342            on_terminate: child.on_terminate.as_ref().map(|r| r.value.clone().into()),
2343            ..Default::default()
2344        });
2345    }
2346    out_children
2347}
2348
2349fn translate_collections_deprecated(collections_in: &Vec<Collection>) -> Vec<fdecl::Collection> {
2350    let mut out_collections = vec![];
2351    for collection in collections_in.iter() {
2352        out_collections.push(fdecl::Collection {
2353            name: Some(collection.name.clone().into()),
2354            durability: Some(collection.durability.clone().into()),
2355            environment: extract_environment_ref_deprecated(collection.environment.as_ref())
2356                .map(|e| e.into()),
2357            allowed_offers: collection.allowed_offers.clone().map(|a| a.into()),
2358            allow_long_names: collection.allow_long_names.clone(),
2359            persistent_storage: collection.persistent_storage.clone(),
2360            ..Default::default()
2361        });
2362    }
2363    out_collections
2364}
2365
2366fn translate_collections(
2367    collections_in: &Vec<ContextSpanned<ContextCollection>>,
2368) -> Vec<fdecl::Collection> {
2369    let mut out_collections = vec![];
2370    for collection_raw in collections_in.iter() {
2371        let collection = &collection_raw.value;
2372        out_collections.push(fdecl::Collection {
2373            name: Some(collection.name.value.clone().into()),
2374            durability: Some(collection.durability.value.clone().into()),
2375            environment: extract_environment_ref(collection.environment.as_ref()).map(|e| e.into()),
2376            allowed_offers: collection.allowed_offers.as_ref().map(|a| a.value.clone().into()),
2377            allow_long_names: collection.allow_long_names.as_ref().map(|a| a.value.into()),
2378            persistent_storage: collection.persistent_storage.as_ref().map(|a| a.value.into()),
2379            ..Default::default()
2380        });
2381    }
2382    out_collections
2383}
2384
2385/// Translates a nested value type to a [`fuchsia.config.decl.ConfigType`]
2386fn translate_nested_value_type(nested_type: &ConfigNestedValueType) -> fdecl::ConfigType {
2387    let layout = match nested_type {
2388        ConfigNestedValueType::Bool {} => fdecl::ConfigTypeLayout::Bool,
2389        ConfigNestedValueType::Uint8 {} => fdecl::ConfigTypeLayout::Uint8,
2390        ConfigNestedValueType::Uint16 {} => fdecl::ConfigTypeLayout::Uint16,
2391        ConfigNestedValueType::Uint32 {} => fdecl::ConfigTypeLayout::Uint32,
2392        ConfigNestedValueType::Uint64 {} => fdecl::ConfigTypeLayout::Uint64,
2393        ConfigNestedValueType::Int8 {} => fdecl::ConfigTypeLayout::Int8,
2394        ConfigNestedValueType::Int16 {} => fdecl::ConfigTypeLayout::Int16,
2395        ConfigNestedValueType::Int32 {} => fdecl::ConfigTypeLayout::Int32,
2396        ConfigNestedValueType::Int64 {} => fdecl::ConfigTypeLayout::Int64,
2397        ConfigNestedValueType::String { .. } => fdecl::ConfigTypeLayout::String,
2398    };
2399    let constraints = match nested_type {
2400        ConfigNestedValueType::String { max_size } => {
2401            vec![fdecl::LayoutConstraint::MaxSize(max_size.get())]
2402        }
2403        _ => vec![],
2404    };
2405    fdecl::ConfigType {
2406        layout,
2407        constraints,
2408        // This optional is not necessary, but without it,
2409        // FIDL compilation complains because of a possible include-cycle.
2410        // Bug: https://fxbug.dev/42145148
2411        parameters: Some(vec![]),
2412    }
2413}
2414
2415/// Translates a value type to a [`fuchsia.sys2.ConfigType`]
2416fn translate_value_type(
2417    value_type: &ConfigValueType,
2418) -> (fdecl::ConfigType, fdecl::ConfigMutability) {
2419    let (layout, source_mutability) = match value_type {
2420        ConfigValueType::Bool { mutability } => (fdecl::ConfigTypeLayout::Bool, mutability),
2421        ConfigValueType::Uint8 { mutability } => (fdecl::ConfigTypeLayout::Uint8, mutability),
2422        ConfigValueType::Uint16 { mutability } => (fdecl::ConfigTypeLayout::Uint16, mutability),
2423        ConfigValueType::Uint32 { mutability } => (fdecl::ConfigTypeLayout::Uint32, mutability),
2424        ConfigValueType::Uint64 { mutability } => (fdecl::ConfigTypeLayout::Uint64, mutability),
2425        ConfigValueType::Int8 { mutability } => (fdecl::ConfigTypeLayout::Int8, mutability),
2426        ConfigValueType::Int16 { mutability } => (fdecl::ConfigTypeLayout::Int16, mutability),
2427        ConfigValueType::Int32 { mutability } => (fdecl::ConfigTypeLayout::Int32, mutability),
2428        ConfigValueType::Int64 { mutability } => (fdecl::ConfigTypeLayout::Int64, mutability),
2429        ConfigValueType::String { mutability, .. } => (fdecl::ConfigTypeLayout::String, mutability),
2430        ConfigValueType::Vector { mutability, .. } => (fdecl::ConfigTypeLayout::Vector, mutability),
2431    };
2432    let (constraints, parameters) = match value_type {
2433        ConfigValueType::String { max_size, .. } => {
2434            (vec![fdecl::LayoutConstraint::MaxSize(max_size.get())], vec![])
2435        }
2436        ConfigValueType::Vector { max_count, element, .. } => {
2437            let nested_type = translate_nested_value_type(element);
2438            (
2439                vec![fdecl::LayoutConstraint::MaxSize(max_count.get())],
2440                vec![fdecl::LayoutParameter::NestedType(nested_type)],
2441            )
2442        }
2443        _ => (vec![], vec![]),
2444    };
2445    let mut mutability = fdecl::ConfigMutability::empty();
2446    if let Some(source_mutability) = source_mutability {
2447        for source in source_mutability {
2448            match source {
2449                ConfigRuntimeSource::Parent => mutability |= fdecl::ConfigMutability::PARENT,
2450            }
2451        }
2452    }
2453    (
2454        fdecl::ConfigType {
2455            layout,
2456            constraints,
2457            // This optional is not necessary, but without it,
2458            // FIDL compilation complains because of a possible include-cycle.
2459            // Bug: https://fxbug.dev/42145148
2460            parameters: Some(parameters),
2461        },
2462        mutability,
2463    )
2464}
2465
2466/// Create the `fdecl::ConfigSchema` from the fields of the `config` block and the config capability
2467/// Use decls.
2468fn translate_config_deprecated(
2469    fields: &Option<BTreeMap<ConfigKey, ConfigValueType>>,
2470    uses: &Option<Vec<Use>>,
2471    package_path: &Option<String>,
2472) -> Result<Option<fdecl::ConfigSchema>, Error> {
2473    let mut use_fields: BTreeMap<ConfigKey, ConfigValueType> = uses
2474        .iter()
2475        .flatten()
2476        .map(|u| {
2477            if u.config.is_none() {
2478                return None;
2479            }
2480            let key = ConfigKey(u.key.clone().expect("key should be set").into());
2481            let config_type =
2482                validate::use_config_to_value_type(u).expect("config type should be valid");
2483            Some((key, config_type))
2484        })
2485        .flatten()
2486        .collect();
2487    for (key, value) in fields.iter().flatten() {
2488        if use_fields.contains_key(key) {
2489            if use_fields.get(key) != Some(&value) {
2490                return Err(Error::validate(format!(
2491                    "Config error: `use` and `config` block contain key '{}' with different types",
2492                    key
2493                )));
2494            }
2495        }
2496        use_fields.insert(key.clone(), value.clone());
2497    }
2498
2499    if use_fields.is_empty() {
2500        return Ok(None);
2501    }
2502
2503    let source = match fields.as_ref().map_or(true, |f| f.is_empty()) {
2504        // If the config block is empty, we are using from capabilities.
2505        true => fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default()),
2506        // We aren't using config capabilities, check for the package path.
2507        _ => {
2508            let Some(package_path) = package_path.as_ref() else {
2509                return Err(Error::invalid_args(
2510                    "can't translate config: no package path for value file",
2511                ));
2512            };
2513            fdecl::ConfigValueSource::PackagePath(package_path.to_owned())
2514        }
2515    };
2516
2517    let mut fidl_fields = vec![];
2518
2519    // Compute a SHA-256 hash from each field
2520    let mut hasher = Sha256::new();
2521
2522    for (key, value) in &use_fields {
2523        let (type_, mutability) = translate_value_type(value);
2524
2525        fidl_fields.push(fdecl::ConfigField {
2526            key: Some(key.to_string()),
2527            type_: Some(type_),
2528            mutability: Some(mutability),
2529            ..Default::default()
2530        });
2531
2532        hasher.update(key.as_str());
2533
2534        value.update_digest(&mut hasher);
2535    }
2536
2537    let hash = hasher.finalize();
2538    let checksum = fdecl::ConfigChecksum::Sha256(*hash.as_ref());
2539
2540    Ok(Some(fdecl::ConfigSchema {
2541        fields: Some(fidl_fields),
2542        checksum: Some(checksum),
2543        value_source: Some(source),
2544        ..Default::default()
2545    }))
2546}
2547
2548/// Create the `fdecl::ConfigSchema` from the fields of the `config` block and the config capability
2549/// Use decls.
2550fn translate_config(
2551    fields: &Option<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>,
2552    uses: &Option<Vec<ContextSpanned<ContextUse>>>,
2553    package_path: &Option<String>,
2554) -> Result<Option<fdecl::ConfigSchema>, Error> {
2555    let mut use_fields: BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>> = uses
2556        .iter()
2557        .flatten()
2558        .filter_map(|u| {
2559            if u.value.config.is_none() {
2560                return None;
2561            }
2562            let key = ConfigKey(u.value.key.clone().expect("key should be set").value.into());
2563
2564            let config_type_raw = validate::use_config_to_value_type_context(&u.value)
2565                .expect("config type should be valid");
2566
2567            let config_type = ContextSpanned { value: config_type_raw, origin: u.origin.clone() };
2568
2569            Some((key, config_type))
2570        })
2571        .collect();
2572
2573    for (key, value) in fields.iter().flatten() {
2574        if use_fields.contains_key(key) {
2575            if use_fields.get(key).map(|v| &v.value) != Some(&value.value) {
2576                return Err(Error::validate_context(
2577                    format!(
2578                        "Config error: `use` and `config` block contain key '{}' with different types",
2579                        key
2580                    ),
2581                    Some(value.origin.clone()),
2582                ));
2583            }
2584        }
2585        use_fields.insert(key.clone(), value.clone());
2586    }
2587
2588    if use_fields.is_empty() {
2589        return Ok(None);
2590    }
2591
2592    let source = match fields.as_ref().map_or(true, |f| f.is_empty()) {
2593        true => fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default()),
2594        _ => {
2595            let Some(package_path) = package_path.as_ref() else {
2596                return Err(Error::invalid_args(
2597                    "can't translate config: no package path for value file",
2598                ));
2599            };
2600            fdecl::ConfigValueSource::PackagePath(package_path.to_owned())
2601        }
2602    };
2603
2604    let mut fidl_fields = vec![];
2605    let mut hasher = Sha256::new();
2606
2607    for (key, value) in &use_fields {
2608        let (type_, mutability) = translate_value_type(&value.value);
2609
2610        fidl_fields.push(fdecl::ConfigField {
2611            key: Some(key.to_string()),
2612            type_: Some(type_),
2613            mutability: Some(mutability),
2614            ..Default::default()
2615        });
2616
2617        hasher.update(key.as_str());
2618        value.value.update_digest(&mut hasher);
2619    }
2620
2621    let hash = hasher.finalize();
2622    let checksum = fdecl::ConfigChecksum::Sha256(*hash.as_ref());
2623
2624    Ok(Some(fdecl::ConfigSchema {
2625        fields: Some(fidl_fields),
2626        checksum: Some(checksum),
2627        value_source: Some(source),
2628        ..Default::default()
2629    }))
2630}
2631
2632fn translate_environments_deprecated(
2633    options: &CompileOptions<'_>,
2634    envs_in: &Vec<Environment>,
2635    all_capability_names: &BTreeSet<&BorrowedName>,
2636) -> Result<Vec<fdecl::Environment>, Error> {
2637    envs_in
2638        .iter()
2639        .map(|env| {
2640            Ok(fdecl::Environment {
2641                name: Some(env.name.clone().into()),
2642                extends: match env.extends {
2643                    Some(EnvironmentExtends::Realm) => Some(fdecl::EnvironmentExtends::Realm),
2644                    Some(EnvironmentExtends::None) => Some(fdecl::EnvironmentExtends::None),
2645                    None => Some(fdecl::EnvironmentExtends::None),
2646                },
2647                runners: env
2648                    .runners
2649                    .as_ref()
2650                    .map(|runners| {
2651                        runners
2652                            .iter()
2653                            .map(|r| translate_runner_registration_deprecated(options, r))
2654                            .collect::<Result<Vec<_>, Error>>()
2655                    })
2656                    .transpose()?,
2657                resolvers: env
2658                    .resolvers
2659                    .as_ref()
2660                    .map(|resolvers| {
2661                        resolvers
2662                            .iter()
2663                            .map(|r| translate_resolver_registration_deprecated(options, r))
2664                            .collect::<Result<Vec<_>, Error>>()
2665                    })
2666                    .transpose()?,
2667                debug_capabilities: env
2668                    .debug
2669                    .as_ref()
2670                    .map(|debug_capabiltities| {
2671                        translate_debug_capabilities_deprecated(
2672                            options,
2673                            debug_capabiltities,
2674                            all_capability_names,
2675                        )
2676                    })
2677                    .transpose()?,
2678                stop_timeout_ms: env.stop_timeout_ms.map(|s| s.0),
2679                ..Default::default()
2680            })
2681        })
2682        .collect()
2683}
2684
2685fn translate_environments(
2686    options: &CompileOptions<'_>,
2687    envs_in: &Vec<ContextSpanned<ContextEnvironment>>,
2688    all_capability_names: &BTreeSet<Name>,
2689) -> Result<Vec<fdecl::Environment>, Error> {
2690    envs_in
2691        .iter()
2692        .map(|cs_env| {
2693            let env = &cs_env.value;
2694            Ok(fdecl::Environment {
2695                name: Some(env.name.value.clone().into()),
2696                extends: match &env.extends {
2697                    Some(spanned) => match spanned.value {
2698                        EnvironmentExtends::Realm => Some(fdecl::EnvironmentExtends::Realm),
2699                        EnvironmentExtends::None => Some(fdecl::EnvironmentExtends::None),
2700                    },
2701                    None => Some(fdecl::EnvironmentExtends::None),
2702                },
2703                runners: env
2704                    .runners
2705                    .as_ref()
2706                    .map(|runners| {
2707                        runners
2708                            .iter()
2709                            .map(|r| translate_runner_registration(options, &r.value))
2710                            .collect::<Result<Vec<_>, Error>>()
2711                    })
2712                    .transpose()?,
2713                resolvers: env
2714                    .resolvers
2715                    .as_ref()
2716                    .map(|resolvers| {
2717                        resolvers
2718                            .iter()
2719                            .map(|r| translate_resolver_registration(options, &r.value))
2720                            .collect::<Result<Vec<_>, Error>>()
2721                    })
2722                    .transpose()?,
2723                debug_capabilities: env
2724                    .debug
2725                    .as_ref()
2726                    .map(|debug_capabiltities| {
2727                        translate_debug_capabilities(
2728                            options,
2729                            debug_capabiltities,
2730                            all_capability_names,
2731                        )
2732                    })
2733                    .transpose()?,
2734                stop_timeout_ms: env.stop_timeout_ms.clone().map(|s| s.value.0),
2735                ..Default::default()
2736            })
2737        })
2738        .collect()
2739}
2740
2741fn translate_runner_registration_deprecated(
2742    options: &CompileOptions<'_>,
2743    reg: &RunnerRegistration,
2744) -> Result<fdecl::RunnerRegistration, Error> {
2745    let (source, _source_dictionary) = extract_single_offer_source_deprecated(options, reg, None)?;
2746    Ok(fdecl::RunnerRegistration {
2747        source_name: Some(reg.runner.clone().into()),
2748        source: Some(source),
2749        target_name: Some(reg.r#as.as_ref().unwrap_or(&reg.runner).clone().into()),
2750        ..Default::default()
2751    })
2752}
2753
2754fn translate_resolver_registration_deprecated(
2755    options: &CompileOptions<'_>,
2756    reg: &ResolverRegistration,
2757) -> Result<fdecl::ResolverRegistration, Error> {
2758    let (source, _source_dictionary) = extract_single_offer_source_deprecated(options, reg, None)?;
2759    Ok(fdecl::ResolverRegistration {
2760        resolver: Some(reg.resolver.clone().into()),
2761        source: Some(source),
2762        scheme: Some(
2763            reg.scheme
2764                .as_str()
2765                .parse::<cm_types::UrlScheme>()
2766                .map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))?
2767                .into(),
2768        ),
2769        ..Default::default()
2770    })
2771}
2772
2773fn translate_debug_capabilities_deprecated(
2774    options: &CompileOptions<'_>,
2775    capabilities: &Vec<DebugRegistration>,
2776    all_capability_names: &BTreeSet<&BorrowedName>,
2777) -> Result<Vec<fdecl::DebugRegistration>, Error> {
2778    let mut out_capabilities = vec![];
2779    for capability in capabilities {
2780        if let Some(n) = capability.protocol() {
2781            let (source, _source_dictionary) = extract_single_offer_source_deprecated(
2782                options,
2783                capability,
2784                Some(all_capability_names),
2785            )?;
2786            let targets = all_target_capability_names_deprecated(capability, capability)
2787                .ok_or_else(|| Error::internal("no capability"))?;
2788            let source_names = n;
2789            for target_name in targets {
2790                // When multiple source names are provided, there is no way to alias each one, so
2791                // source_name == target_name.
2792                // When one source name is provided, source_name may be aliased to a different
2793                // target_name, so we source_names[0] to derive the source_name.
2794                //
2795                // TODO: This logic could be simplified to use iter::zip() if
2796                // extract_all_targets_for_each_child returned separate vectors for targets and
2797                // target_names instead of the cross product of them.
2798                let source_name = if source_names.len() == 1 {
2799                    *source_names.iter().next().unwrap()
2800                } else {
2801                    target_name
2802                };
2803                out_capabilities.push(fdecl::DebugRegistration::Protocol(
2804                    fdecl::DebugProtocolRegistration {
2805                        source: Some(source.clone()),
2806                        source_name: Some(source_name.to_string()),
2807                        target_name: Some(target_name.to_string()),
2808                        ..Default::default()
2809                    },
2810                ));
2811            }
2812        }
2813    }
2814    Ok(out_capabilities)
2815}
2816
2817fn translate_runner_registration(
2818    options: &CompileOptions<'_>,
2819    reg: &ContextRunnerRegistration,
2820) -> Result<fdecl::RunnerRegistration, Error> {
2821    let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
2822    Ok(fdecl::RunnerRegistration {
2823        source_name: Some(reg.runner.value.clone().into()),
2824        source: Some(source),
2825        target_name: Some(
2826            reg.r#as.as_ref().map(|s| &s.value).unwrap_or(&reg.runner.value).to_string(),
2827        ),
2828        ..Default::default()
2829    })
2830}
2831
2832fn translate_resolver_registration(
2833    options: &CompileOptions<'_>,
2834    reg: &ContextResolverRegistration,
2835) -> Result<fdecl::ResolverRegistration, Error> {
2836    let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
2837    Ok(fdecl::ResolverRegistration {
2838        resolver: Some(reg.resolver.value.clone().into()),
2839        source: Some(source),
2840        scheme: Some(
2841            reg.scheme
2842                .value
2843                .as_str()
2844                .parse::<cm_types::UrlScheme>()
2845                .map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))?
2846                .into(),
2847        ),
2848        ..Default::default()
2849    })
2850}
2851
2852fn translate_debug_capabilities(
2853    options: &CompileOptions<'_>,
2854    capabilities: &Vec<ContextSpanned<ContextDebugRegistration>>,
2855    all_capability_names: &BTreeSet<Name>,
2856) -> Result<Vec<fdecl::DebugRegistration>, Error> {
2857    let mut out_capabilities = vec![];
2858    for spanned_capability in capabilities {
2859        let capability = &spanned_capability.value;
2860        if let Some(n) = capability.protocol() {
2861            let (source, _source_dictionary) =
2862                extract_single_offer_source(options, capability, Some(all_capability_names))?;
2863            let targets = all_target_capability_names(capability, capability)
2864                .ok_or_else(|| Error::internal("no capability"))?;
2865            let source_names = n;
2866            for target_name in targets {
2867                // When multiple source names are provided, there is no way to alias each one, so
2868                // source_name == target_name.
2869                // When one source name is provided, source_name may be aliased to a different
2870                // target_name, so we source_names[0] to derive the source_name.
2871                //
2872                // TODO: This logic could be simplified to use iter::zip() if
2873                // extract_all_targets_for_each_child returned separate vectors for targets and
2874                // target_names instead of the cross product of them.
2875                let source_name = if source_names.value.len() == 1 {
2876                    *source_names.value.iter().next().unwrap()
2877                } else {
2878                    target_name
2879                };
2880                out_capabilities.push(fdecl::DebugRegistration::Protocol(
2881                    fdecl::DebugProtocolRegistration {
2882                        source: Some(source.clone()),
2883                        source_name: Some(source_name.to_string()),
2884                        target_name: Some(target_name.to_string()),
2885                        ..Default::default()
2886                    },
2887                ));
2888            }
2889        }
2890    }
2891    Ok(out_capabilities)
2892}
2893
2894fn extract_use_source_deprecated(
2895    options: &CompileOptions<'_>,
2896    in_obj: &Use,
2897    all_capability_names: &BTreeSet<&BorrowedName>,
2898    all_children_names: &BTreeSet<&BorrowedName>,
2899    all_collection_names: Option<&BTreeSet<&BorrowedName>>,
2900) -> Result<(fdecl::Ref, Option<String>), Error> {
2901    let ref_ = match in_obj.from.as_ref() {
2902        Some(UseFromRef::Parent) => fdecl::Ref::Parent(fdecl::ParentRef {}),
2903        Some(UseFromRef::Framework) => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2904        Some(UseFromRef::Debug) => fdecl::Ref::Debug(fdecl::DebugRef {}),
2905        Some(UseFromRef::Self_) => fdecl::Ref::Self_(fdecl::SelfRef {}),
2906        Some(UseFromRef::Named(name)) => {
2907            if all_capability_names.contains(&name.as_ref()) {
2908                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
2909            } else if all_children_names.contains(&name.as_ref()) {
2910                fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
2911            } else if all_collection_names.is_some()
2912                && all_collection_names.unwrap().contains(&name.as_ref())
2913            {
2914                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
2915            } else {
2916                return Err(Error::internal(format!(
2917                    "use source \"{:?}\" not supported for \"use from\"",
2918                    name
2919                )));
2920            }
2921        }
2922        Some(UseFromRef::Dictionary(d)) => {
2923            return Ok(dictionary_ref_to_source(&d));
2924        }
2925        None => fdecl::Ref::Parent(fdecl::ParentRef {}), // Default value.
2926    };
2927    Ok((ref_, None))
2928}
2929
2930fn extract_use_source(
2931    options: &CompileOptions<'_>,
2932    in_obj: &ContextUse,
2933    all_capability_names: &BTreeSet<Name>,
2934    all_children_names: &BTreeSet<Name>,
2935    all_collection_names: Option<&BTreeSet<Name>>,
2936) -> Result<(fdecl::Ref, Option<String>), Error> {
2937    let ref_ = match in_obj.from.as_ref() {
2938        Some(spanned) => match &spanned.value {
2939            UseFromRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
2940            UseFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2941            UseFromRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
2942            UseFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
2943            UseFromRef::Named(name) => {
2944                if all_children_names.contains::<BorrowedName>(name.as_ref()) {
2945                    fdecl::Ref::Child(fdecl::ChildRef {
2946                        name: name.clone().into(),
2947                        collection: None,
2948                    })
2949                } else if all_collection_names.is_some()
2950                    && all_collection_names.unwrap().contains::<BorrowedName>(name.as_ref())
2951                {
2952                    fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
2953                } else if all_capability_names.contains::<BorrowedName>(name.as_ref()) {
2954                    fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
2955                } else {
2956                    return Err(Error::validate_context(
2957                        format!(
2958                            "use: from value \"{}\" does not match any child, collection, or capability",
2959                            name
2960                        ),
2961                        Some(spanned.origin.clone()),
2962                    ));
2963                }
2964            }
2965            UseFromRef::Dictionary(d) => {
2966                return Ok(dictionary_ref_to_source(&d));
2967            }
2968        },
2969        None => fdecl::Ref::Parent(fdecl::ParentRef {}), // Default value.
2970    };
2971    Ok((ref_, None))
2972}
2973
2974fn extract_use_availability_deprecated(in_obj: &Use) -> Result<fdecl::Availability, Error> {
2975    match in_obj.availability.as_ref() {
2976        Some(Availability::Required) | None => Ok(fdecl::Availability::Required),
2977        Some(Availability::Optional) => Ok(fdecl::Availability::Optional),
2978        Some(Availability::Transitional) => Ok(fdecl::Availability::Transitional),
2979        Some(Availability::SameAsTarget) => Err(Error::internal(
2980            "availability \"same_as_target\" not supported for use declarations",
2981        )),
2982    }
2983}
2984
2985fn extract_use_availability(in_obj: &ContextUse) -> Result<fdecl::Availability, Error> {
2986    match in_obj.availability.as_ref() {
2987        Some(spanned) => match spanned.value {
2988            Availability::Required => Ok(fdecl::Availability::Required),
2989            Availability::Optional => Ok(fdecl::Availability::Optional),
2990            Availability::Transitional => Ok(fdecl::Availability::Transitional),
2991            Availability::SameAsTarget => Err(Error::internal(
2992                "availability \"same_as_target\" not supported for use declarations",
2993            )),
2994        },
2995        None => Ok(fdecl::Availability::Required),
2996    }
2997}
2998
2999fn extract_use_subdir_deprecated(in_obj: &Use) -> Option<cm::RelativePath> {
3000    in_obj.subdir.clone()
3001}
3002
3003fn extract_use_subdir(in_obj: &ContextUse) -> Option<cm::RelativePath> {
3004    in_obj.subdir.clone().map(|s| s.value)
3005}
3006
3007fn extract_expose_subdir_deprecated(in_obj: &Expose) -> Option<cm::RelativePath> {
3008    in_obj.subdir.clone()
3009}
3010
3011fn extract_expose_subdir(in_obj: &ContextExpose) -> Option<cm::RelativePath> {
3012    in_obj.subdir.clone().map(|s| s.value)
3013}
3014
3015fn extract_offer_subdir_deprecated(in_obj: &Offer) -> Option<cm::RelativePath> {
3016    in_obj.subdir.clone()
3017}
3018
3019fn extract_offer_subdir(in_obj: &ContextOffer) -> Option<cm::RelativePath> {
3020    in_obj.subdir.clone().map(|s| s.value)
3021}
3022
3023fn extract_expose_rights_deprecated(in_obj: &Expose) -> Result<Option<fio::Operations>, Error> {
3024    match in_obj.rights.as_ref() {
3025        Some(rights_tokens) => {
3026            let mut rights = Vec::new();
3027            for token in rights_tokens.0.iter() {
3028                rights.append(&mut token.expand())
3029            }
3030            if rights.is_empty() {
3031                return Err(Error::missing_rights(
3032                    "Rights provided to expose are not well formed.",
3033                ));
3034            }
3035            let mut seen_rights = BTreeSet::new();
3036            let mut operations: fio::Operations = fio::Operations::empty();
3037            for right in rights.iter() {
3038                if seen_rights.contains(&right) {
3039                    return Err(Error::duplicate_rights(
3040                        "Rights provided to expose are not well formed.",
3041                    ));
3042                }
3043                seen_rights.insert(right);
3044                operations |= *right;
3045            }
3046
3047            Ok(Some(operations))
3048        }
3049        // Unlike use rights, expose rights can take a None value
3050        None => Ok(None),
3051    }
3052}
3053
3054fn extract_expose_rights(in_obj: &ContextExpose) -> Result<Option<fio::Operations>, Error> {
3055    match in_obj.rights.as_ref() {
3056        Some(spanned) => {
3057            let rights_tokens = &spanned.value;
3058            let mut rights = Vec::new();
3059            for token in rights_tokens.0.iter() {
3060                rights.append(&mut token.expand())
3061            }
3062            if rights.is_empty() {
3063                return Err(Error::missing_rights(
3064                    "Rights provided to expose are not well formed.",
3065                ));
3066            }
3067            let mut seen_rights = BTreeSet::new();
3068            let mut operations: fio::Operations = fio::Operations::empty();
3069            for right in rights.iter() {
3070                if seen_rights.contains(&right) {
3071                    return Err(Error::duplicate_rights(
3072                        "Rights provided to expose are not well formed.",
3073                    ));
3074                }
3075                seen_rights.insert(right);
3076                operations |= *right;
3077            }
3078
3079            Ok(Some(operations))
3080        }
3081        // Unlike use rights, expose rights can take a None value
3082        None => Ok(None),
3083    }
3084}
3085
3086fn expose_source_from_ref_deprecated(
3087    options: &CompileOptions<'_>,
3088    reference: &ExposeFromRef,
3089    all_capability_names: Option<&BTreeSet<&BorrowedName>>,
3090    all_collections: Option<&BTreeSet<&BorrowedName>>,
3091) -> (fdecl::Ref, Option<String>) {
3092    let ref_ = match reference {
3093        ExposeFromRef::Named(name) => {
3094            if all_capability_names.is_some()
3095                && all_capability_names.unwrap().contains(&name.as_ref())
3096            {
3097                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
3098            } else if all_collections.is_some() && all_collections.unwrap().contains(&name.as_ref())
3099            {
3100                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
3101            } else {
3102                fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
3103            }
3104        }
3105        ExposeFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3106        ExposeFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
3107        ExposeFromRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
3108        ExposeFromRef::Dictionary(d) => {
3109            return dictionary_ref_to_source(&d);
3110        }
3111    };
3112    (ref_, None)
3113}
3114
3115fn expose_source_from_ref(
3116    options: &CompileOptions<'_>,
3117    reference: &ExposeFromRef,
3118    all_capability_names: Option<&BTreeSet<Name>>,
3119    all_collections: Option<&BTreeSet<Name>>,
3120) -> (fdecl::Ref, Option<String>) {
3121    let ref_ = match reference {
3122        ExposeFromRef::Named(name) => {
3123            if all_capability_names.is_some()
3124                && all_capability_names.unwrap().contains::<BorrowedName>(name.as_ref())
3125            {
3126                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
3127            } else if all_collections.is_some()
3128                && all_collections.unwrap().contains::<BorrowedName>(name.as_ref())
3129            {
3130                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
3131            } else {
3132                fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
3133            }
3134        }
3135        ExposeFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3136        ExposeFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
3137        ExposeFromRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
3138        ExposeFromRef::Dictionary(d) => {
3139            return dictionary_ref_to_source(&d);
3140        }
3141    };
3142    (ref_, None)
3143}
3144
3145fn extract_single_expose_source_deprecated(
3146    options: &CompileOptions<'_>,
3147    in_obj: &Expose,
3148    all_capability_names: Option<&BTreeSet<&BorrowedName>>,
3149) -> Result<(fdecl::Ref, Option<String>), Error> {
3150    match &in_obj.from {
3151        OneOrMany::One(reference) => {
3152            Ok(expose_source_from_ref_deprecated(options, &reference, all_capability_names, None))
3153        }
3154        OneOrMany::Many(many) => Err(Error::internal(format!(
3155            "multiple unexpected \"from\" clauses for \"expose\": {:?}",
3156            many
3157        ))),
3158    }
3159}
3160
3161fn extract_single_expose_source(
3162    options: &CompileOptions<'_>,
3163    in_obj: &ContextExpose,
3164    all_capability_names: Option<&BTreeSet<Name>>,
3165) -> Result<(fdecl::Ref, Option<String>), Error> {
3166    match &in_obj.from.value {
3167        OneOrMany::One(reference) => {
3168            Ok(expose_source_from_ref(options, &reference, all_capability_names, None))
3169        }
3170        OneOrMany::Many(many) => Err(Error::internal(format!(
3171            "multiple unexpected \"from\" clauses for \"expose\": {:?}",
3172            many
3173        ))),
3174    }
3175}
3176
3177fn extract_all_expose_sources_deprecated(
3178    options: &CompileOptions<'_>,
3179    in_obj: &Expose,
3180    all_collections: Option<&BTreeSet<&BorrowedName>>,
3181) -> Vec<(fdecl::Ref, Option<String>)> {
3182    in_obj
3183        .from
3184        .iter()
3185        .map(|e| expose_source_from_ref_deprecated(options, e, None, all_collections))
3186        .collect()
3187}
3188
3189fn extract_all_expose_sources(
3190    options: &CompileOptions<'_>,
3191    in_obj: &ContextExpose,
3192    all_collections: Option<&BTreeSet<Name>>,
3193) -> Vec<(fdecl::Ref, Option<String>)> {
3194    in_obj
3195        .from
3196        .value
3197        .iter()
3198        .map(|e| expose_source_from_ref(options, e, None, all_collections))
3199        .collect()
3200}
3201
3202fn extract_offer_rights_deprecated(in_obj: &Offer) -> Result<Option<fio::Operations>, Error> {
3203    match in_obj.rights.as_ref() {
3204        Some(rights_tokens) => {
3205            let mut rights = Vec::new();
3206            for token in rights_tokens.0.iter() {
3207                rights.append(&mut token.expand())
3208            }
3209            if rights.is_empty() {
3210                return Err(Error::missing_rights("Rights provided to offer are not well formed."));
3211            }
3212            let mut seen_rights = BTreeSet::new();
3213            let mut operations: fio::Operations = fio::Operations::empty();
3214            for right in rights.iter() {
3215                if seen_rights.contains(&right) {
3216                    return Err(Error::duplicate_rights(
3217                        "Rights provided to offer are not well formed.",
3218                    ));
3219                }
3220                seen_rights.insert(right);
3221                operations |= *right;
3222            }
3223
3224            Ok(Some(operations))
3225        }
3226        // Unlike use rights, offer rights can take a None value
3227        None => Ok(None),
3228    }
3229}
3230
3231fn extract_offer_rights(in_obj: &ContextOffer) -> Result<Option<fio::Operations>, Error> {
3232    match in_obj.rights.as_ref() {
3233        Some(cs_rights) => {
3234            let rights_token = &cs_rights.value;
3235            let mut rights = Vec::new();
3236            for token in rights_token.0.iter() {
3237                rights.append(&mut token.expand())
3238            }
3239            if rights.is_empty() {
3240                return Err(Error::missing_rights("Rights provided to offer are not well formed."));
3241            }
3242            let mut seen_rights = BTreeSet::new();
3243            let mut operations: fio::Operations = fio::Operations::empty();
3244            for right in rights.iter() {
3245                if seen_rights.contains(&right) {
3246                    return Err(Error::duplicate_rights(
3247                        "Rights provided to offer are not well formed.",
3248                    ));
3249                }
3250                seen_rights.insert(right);
3251                operations |= *right;
3252            }
3253
3254            Ok(Some(operations))
3255        }
3256        // Unlike use rights, offer rights can take a None value
3257        None => Ok(None),
3258    }
3259}
3260
3261fn extract_single_offer_source_deprecated<T>(
3262    options: &CompileOptions<'_>,
3263    in_obj: &T,
3264    all_capability_names: Option<&BTreeSet<&BorrowedName>>,
3265) -> Result<(fdecl::Ref, Option<String>), Error>
3266where
3267    T: FromClause,
3268{
3269    match in_obj.from_() {
3270        OneOrMany::One(reference) => {
3271            Ok(any_ref_to_decl_deprecated(options, reference, all_capability_names, None))
3272        }
3273        many => {
3274            return Err(Error::internal(format!(
3275                "multiple unexpected \"from\" clauses for \"offer\": {}",
3276                many
3277            )));
3278        }
3279    }
3280}
3281
3282fn extract_single_offer_source<T>(
3283    options: &CompileOptions<'_>,
3284    in_obj: &T,
3285    all_capability_names: Option<&BTreeSet<Name>>,
3286) -> Result<(fdecl::Ref, Option<String>), Error>
3287where
3288    T: FromClauseContext,
3289{
3290    match in_obj.from_().value {
3291        OneOrMany::One(reference) => {
3292            Ok(any_ref_to_decl(options, reference, all_capability_names, None))
3293        }
3294        many => {
3295            return Err(Error::internal(format!(
3296                "multiple unexpected \"from\" clauses for \"offer\": {}",
3297                many
3298            )));
3299        }
3300    }
3301}
3302
3303fn extract_all_offer_sources_deprecated<T: FromClause>(
3304    options: &CompileOptions<'_>,
3305    in_obj: &T,
3306    all_capability_names: &BTreeSet<&BorrowedName>,
3307    all_collections: &BTreeSet<&BorrowedName>,
3308) -> Vec<(fdecl::Ref, Option<String>)> {
3309    in_obj
3310        .from_()
3311        .into_iter()
3312        .map(|r| {
3313            any_ref_to_decl_deprecated(
3314                options,
3315                r.clone(),
3316                Some(all_capability_names),
3317                Some(all_collections),
3318            )
3319        })
3320        .collect()
3321}
3322
3323fn extract_all_offer_sources<T: FromClauseContext>(
3324    options: &CompileOptions<'_>,
3325    in_obj: &T,
3326    all_capability_names: &BTreeSet<Name>,
3327    all_collections: &BTreeSet<Name>,
3328) -> Vec<(fdecl::Ref, Option<String>)> {
3329    in_obj
3330        .from_()
3331        .value
3332        .into_iter()
3333        .map(|r| {
3334            any_ref_to_decl(options, r.clone(), Some(all_capability_names), Some(all_collections))
3335        })
3336        .collect()
3337}
3338
3339fn translate_target_ref_deprecated(
3340    options: &CompileOptions<'_>,
3341    reference: AnyRef<'_>,
3342    all_children: &BTreeSet<&BorrowedName>,
3343    all_collections: &BTreeSet<&BorrowedName>,
3344    all_capabilities: &BTreeSet<&BorrowedName>,
3345    target_availability: Option<&TargetAvailability>,
3346) -> Result<Option<fdecl::Ref>, Error> {
3347    match reference {
3348        AnyRef::Named(name) if all_children.contains(name) => {
3349            Ok(Some(fdecl::Ref::Child(fdecl::ChildRef {
3350                name: name.to_string(),
3351                collection: None,
3352            })))
3353        }
3354        AnyRef::Named(name) if all_collections.contains(name) => {
3355            Ok(Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })))
3356        }
3357        AnyRef::Named(name) if all_capabilities.contains(name) => {
3358            Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3359        }
3360        AnyRef::OwnDictionary(name) if all_capabilities.contains(name) => {
3361            Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3362        }
3363        AnyRef::Named(_) | AnyRef::OwnDictionary(_)
3364            if target_availability == Some(&TargetAvailability::Unknown) =>
3365        {
3366            Ok(None)
3367        }
3368        AnyRef::Named(_) => Err(Error::internal(format!("dangling reference: \"{}\"", reference))),
3369        _ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))),
3370    }
3371}
3372
3373fn translate_target_ref(
3374    options: &CompileOptions<'_>,
3375    reference: AnyRef<'_>,
3376    all_children: &BTreeSet<Name>,
3377    all_collections: &BTreeSet<Name>,
3378    all_capabilities: &BTreeSet<Name>,
3379    target_availability: Option<&TargetAvailability>,
3380) -> Result<Option<fdecl::Ref>, Error> {
3381    match reference {
3382        AnyRef::Named(name) if all_children.contains::<BorrowedName>(name) => {
3383            Ok(Some(fdecl::Ref::Child(fdecl::ChildRef {
3384                name: name.to_string(),
3385                collection: None,
3386            })))
3387        }
3388        AnyRef::Named(name) if all_collections.contains::<BorrowedName>(name) => {
3389            Ok(Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })))
3390        }
3391        AnyRef::Named(name) if all_capabilities.contains::<BorrowedName>(name) => {
3392            Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3393        }
3394        AnyRef::OwnDictionary(name) if all_capabilities.contains::<BorrowedName>(name) => {
3395            Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3396        }
3397        AnyRef::Named(_) | AnyRef::OwnDictionary(_)
3398            if target_availability == Some(&TargetAvailability::Unknown) =>
3399        {
3400            Ok(None)
3401        }
3402        AnyRef::Named(_) => Err(Error::internal(format!("dangling reference: \"{}\"", reference))),
3403        _ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))),
3404    }
3405}
3406
3407// Return a list of (source, source capability id, source dictionary, target,
3408// target capability id) expressed in the `offer`.
3409fn extract_offer_sources_and_targets_deprecated<'a>(
3410    options: &CompileOptions<'_>,
3411    offer: &'a Offer,
3412    source_names: OneOrMany<&'a BorrowedName>,
3413    all_capability_names: &BTreeSet<&'a BorrowedName>,
3414    all_children: &BTreeSet<&'a BorrowedName>,
3415    all_collections: &BTreeSet<&'a BorrowedName>,
3416) -> Result<Vec<(fdecl::Ref, Option<String>, &'a BorrowedName, fdecl::Ref, &'a BorrowedName)>, Error>
3417{
3418    let mut out = vec![];
3419
3420    let sources =
3421        extract_all_offer_sources_deprecated(options, offer, all_capability_names, all_collections);
3422    let target_names = all_target_capability_names_deprecated(offer, offer)
3423        .ok_or_else(|| Error::internal("no capability".to_string()))?;
3424
3425    for (source, source_dictionary) in sources {
3426        for to in &offer.to {
3427            for target_name in &target_names {
3428                // When multiple source names are provided, there is no way to alias each one,
3429                // so we can assume source_name == target_name.  When one source name is provided,
3430                // source_name may be aliased to a different target_name, so we use
3431                // source_names[0] to obtain the source_name.
3432                let source_name = if source_names.len() == 1 {
3433                    source_names.iter().next().unwrap()
3434                } else {
3435                    target_name
3436                };
3437                if let Some(target) = translate_target_ref_deprecated(
3438                    options,
3439                    to.into(),
3440                    all_children,
3441                    all_collections,
3442                    all_capability_names,
3443                    offer.target_availability.as_ref(),
3444                )? {
3445                    out.push((
3446                        source.clone(),
3447                        source_dictionary.clone(),
3448                        *source_name,
3449                        target.clone(),
3450                        *target_name,
3451                    ));
3452                }
3453            }
3454        }
3455    }
3456    Ok(out)
3457}
3458
3459// Return a list of (source, source capability id, source dictionary, target,
3460// target capability id) expressed in the `offer`.
3461fn extract_offer_sources_and_targets<'a>(
3462    options: &CompileOptions<'_>,
3463    offer: &'a ContextOffer,
3464    source_names: OneOrMany<&'a BorrowedName>,
3465    all_capability_names: &BTreeSet<Name>,
3466    all_children: &BTreeSet<Name>,
3467    all_collections: &BTreeSet<Name>,
3468) -> Result<Vec<(fdecl::Ref, Option<String>, &'a BorrowedName, fdecl::Ref, &'a BorrowedName)>, Error>
3469{
3470    let mut out = vec![];
3471
3472    let sources = extract_all_offer_sources(options, offer, all_capability_names, all_collections);
3473    let target_names = all_target_capability_names(offer, offer)
3474        .ok_or_else(|| Error::internal("no capability".to_string()))?;
3475
3476    for (source, source_dictionary) in sources {
3477        for to in &offer.to.value {
3478            for target_name in &target_names {
3479                // When multiple source names are provided, there is no way to alias each one,
3480                // so we can assume source_name == target_name.  When one source name is provided,
3481                // source_name may be aliased to a different target_name, so we use
3482                // source_names[0] to obtain the source_name.
3483                let source_name = if source_names.len() == 1 {
3484                    source_names.iter().next().unwrap()
3485                } else {
3486                    target_name
3487                };
3488                if let Some(target) = translate_target_ref(
3489                    options,
3490                    to.into(),
3491                    all_children,
3492                    all_collections,
3493                    all_capability_names,
3494                    offer.target_availability.clone().map(|s| s.value).as_ref(),
3495                )? {
3496                    out.push((
3497                        source.clone(),
3498                        source_dictionary.clone(),
3499                        *source_name,
3500                        target.clone(),
3501                        *target_name,
3502                    ));
3503                }
3504            }
3505        }
3506    }
3507    Ok(out)
3508}
3509
3510/// Return the target paths specified in the given use declaration.
3511fn all_target_use_paths_deprecated<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<Path>>
3512where
3513    T: CapabilityClause,
3514    U: PathClause,
3515{
3516    if let Some(n) = in_obj.service() {
3517        Some(svc_paths_from_names_deprecated(n, to_obj))
3518    } else if let Some(n) = in_obj.protocol() {
3519        Some(svc_paths_from_names_deprecated(n, to_obj))
3520    } else if let Some(_) = in_obj.directory() {
3521        let path = to_obj.path().expect("no path on use directory");
3522        Some(OneOrMany::One(path.clone()))
3523    } else if let Some(_) = in_obj.storage() {
3524        let path = to_obj.path().expect("no path on use storage");
3525        Some(OneOrMany::One(path.clone()))
3526    } else if let Some(_) = in_obj.event_stream() {
3527        let default_path = Path::new("/svc/fuchsia.component.EventStream").unwrap();
3528        let path = to_obj.path().unwrap_or(&default_path);
3529        Some(OneOrMany::One(path.clone()))
3530    } else {
3531        None
3532    }
3533}
3534
3535/// Return the target paths specified in the given use declaration.
3536fn all_target_use_paths<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<Path>>
3537where
3538    T: ContextCapabilityClause,
3539    U: ContextPathClause,
3540{
3541    if let Some(n) = in_obj.service() {
3542        Some(svc_paths_from_names(n.value, to_obj))
3543    } else if let Some(n) = in_obj.protocol() {
3544        Some(svc_paths_from_names(n.value, to_obj))
3545    } else if let Some(_) = in_obj.directory() {
3546        let path = &to_obj.path().expect("no path on use directory").value;
3547        Some(OneOrMany::One(path.clone()))
3548    } else if let Some(_) = in_obj.storage() {
3549        let path = &to_obj.path().expect("no path on use storage").value;
3550        Some(OneOrMany::One(path.clone()))
3551    } else if let Some(_) = in_obj.event_stream() {
3552        let default_path = Path::new("/svc/fuchsia.component.EventStream").unwrap();
3553        let path = to_obj.path().map(|s| &s.value).unwrap_or(&default_path);
3554        Some(OneOrMany::One(path.clone()))
3555    } else {
3556        None
3557    }
3558}
3559
3560/// Returns the list of paths derived from a `use` declaration with `names` and `to_obj`. `to_obj`
3561/// must be a declaration that has a `path` clause.
3562fn svc_paths_from_names_deprecated<T>(
3563    names: OneOrMany<&BorrowedName>,
3564    to_obj: &T,
3565) -> OneOrMany<Path>
3566where
3567    T: PathClause,
3568{
3569    match names {
3570        OneOrMany::One(n) => {
3571            if let Some(path) = to_obj.path() {
3572                OneOrMany::One(path.clone())
3573            } else {
3574                OneOrMany::One(format!("/svc/{}", n).parse().unwrap())
3575            }
3576        }
3577        OneOrMany::Many(v) => {
3578            let many = v.iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect();
3579            OneOrMany::Many(many)
3580        }
3581    }
3582}
3583
3584/// Returns the list of paths derived from a `use` declaration with `names` and `to_obj`. `to_obj`
3585/// must be a declaration that has a `path` clause.
3586fn svc_paths_from_names<T>(names: OneOrMany<&BorrowedName>, to_obj: &T) -> OneOrMany<Path>
3587where
3588    T: ContextPathClause,
3589{
3590    match names {
3591        OneOrMany::One(n) => {
3592            if let Some(path) = to_obj.path() {
3593                OneOrMany::One(path.value.clone())
3594            } else {
3595                OneOrMany::One(format!("/svc/{}", n).parse().unwrap())
3596            }
3597        }
3598        OneOrMany::Many(v) => {
3599            let many = v.iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect();
3600            OneOrMany::Many(many)
3601        }
3602    }
3603}
3604
3605/// Return the single target path specified in the given use declaration.
3606fn one_target_use_path_deprecated<T, U>(in_obj: &T, to_obj: &U) -> Result<Path, Error>
3607where
3608    T: CapabilityClause,
3609    U: PathClause,
3610{
3611    match all_target_use_paths_deprecated(in_obj, to_obj) {
3612        Some(OneOrMany::One(target_name)) => Ok(target_name),
3613        Some(OneOrMany::Many(_)) => {
3614            Err(Error::internal("expecting one capability, but multiple provided"))
3615        }
3616        _ => Err(Error::internal("expecting one capability, but none provided")),
3617    }
3618}
3619
3620/// Return the single target path specified in the given use declaration.
3621fn one_target_use_path<T, U>(in_obj: &T, to_obj: &U) -> Result<Path, Error>
3622where
3623    T: ContextCapabilityClause,
3624    U: ContextPathClause,
3625{
3626    match all_target_use_paths(in_obj, to_obj) {
3627        Some(OneOrMany::One(target_name)) => Ok(target_name),
3628        Some(OneOrMany::Many(_)) => {
3629            Err(Error::internal("expecting one capability, but multiple provided"))
3630        }
3631        _ => Err(Error::internal("expecting one capability, but none provided")),
3632    }
3633}
3634
3635/// Return the target names or paths specified in the given capability.
3636fn all_target_capability_names_deprecated<'a, T, U>(
3637    in_obj: &'a T,
3638    to_obj: &'a U,
3639) -> Option<OneOrMany<&'a BorrowedName>>
3640where
3641    T: CapabilityClause,
3642    U: AsClause + PathClause,
3643{
3644    if let Some(as_) = to_obj.r#as() {
3645        // We've already validated that when `as` is specified, only 1 source id exists.
3646        Some(OneOrMany::One(as_))
3647    } else {
3648        if let Some(n) = in_obj.service() {
3649            Some(n)
3650        } else if let Some(n) = in_obj.protocol() {
3651            Some(n)
3652        } else if let Some(n) = in_obj.directory() {
3653            Some(n)
3654        } else if let Some(n) = in_obj.storage() {
3655            Some(n)
3656        } else if let Some(n) = in_obj.runner() {
3657            Some(n)
3658        } else if let Some(n) = in_obj.resolver() {
3659            Some(n)
3660        } else if let Some(n) = in_obj.event_stream() {
3661            Some(n)
3662        } else if let Some(n) = in_obj.dictionary() {
3663            Some(n)
3664        } else if let Some(n) = in_obj.config() {
3665            Some(n)
3666        } else {
3667            None
3668        }
3669    }
3670}
3671
3672/// Return the target names or paths specified in the given capability.
3673fn all_target_capability_names<'a, T, U>(
3674    in_obj: &'a T,
3675    to_obj: &'a U,
3676) -> Option<OneOrMany<&'a BorrowedName>>
3677where
3678    T: ContextCapabilityClause,
3679    U: AsClauseContext + ContextPathClause,
3680{
3681    if let Some(as_) = to_obj.r#as() {
3682        // We've already validated that when `as` is specified, only 1 source id exists.
3683        Some(OneOrMany::One(as_.value))
3684    } else {
3685        if let Some(n) = in_obj.service() {
3686            Some(n.value)
3687        } else if let Some(n) = in_obj.protocol() {
3688            Some(n.value)
3689        } else if let Some(n) = in_obj.directory() {
3690            Some(n.value)
3691        } else if let Some(n) = in_obj.storage() {
3692            Some(n.value)
3693        } else if let Some(n) = in_obj.runner() {
3694            Some(n.value)
3695        } else if let Some(n) = in_obj.resolver() {
3696            Some(n.value)
3697        } else if let Some(n) = in_obj.event_stream() {
3698            Some(n.value)
3699        } else if let Some(n) = in_obj.dictionary() {
3700            Some(n.value)
3701        } else if let Some(n) = in_obj.config() {
3702            Some(n.value)
3703        } else {
3704            None
3705        }
3706    }
3707}
3708
3709fn extract_expose_target_deprecated(in_obj: &Expose) -> fdecl::Ref {
3710    match &in_obj.to {
3711        Some(ExposeToRef::Parent) => fdecl::Ref::Parent(fdecl::ParentRef {}),
3712        Some(ExposeToRef::Framework) => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3713        None => fdecl::Ref::Parent(fdecl::ParentRef {}),
3714    }
3715}
3716
3717fn extract_expose_target(in_obj: &ContextExpose) -> fdecl::Ref {
3718    match &in_obj.to {
3719        Some(spanned) => match &spanned.value {
3720            ExposeToRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
3721            ExposeToRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3722        },
3723        None => fdecl::Ref::Parent(fdecl::ParentRef {}),
3724    }
3725}
3726
3727fn extract_environment_ref_deprecated(r: Option<&EnvironmentRef>) -> Option<cm::Name> {
3728    r.map(|r| {
3729        let EnvironmentRef::Named(name) = r;
3730        name.clone()
3731    })
3732}
3733
3734fn extract_environment_ref(r: Option<&ContextSpanned<EnvironmentRef>>) -> Option<cm::Name> {
3735    r.map(|r| {
3736        let EnvironmentRef::Named(name) = &r.value;
3737        name.clone()
3738    })
3739}
3740
3741pub fn translate_capabilities_deprecated(
3742    options: &CompileOptions<'_>,
3743    capabilities_in: &Vec<Capability>,
3744    as_builtin: bool,
3745) -> Result<Vec<fdecl::Capability>, Error> {
3746    let mut out_capabilities = vec![];
3747    for capability in capabilities_in {
3748        if let Some(service) = &capability.service {
3749            for n in service.iter() {
3750                let source_path = match as_builtin {
3751                    true => None,
3752                    false => Some(
3753                        capability
3754                            .path
3755                            .clone()
3756                            .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3757                            .into(),
3758                    ),
3759                };
3760                out_capabilities.push(fdecl::Capability::Service(fdecl::Service {
3761                    name: Some(n.clone().into()),
3762                    source_path,
3763                    ..Default::default()
3764                }));
3765            }
3766        } else if let Some(protocol) = &capability.protocol {
3767            for n in protocol.iter() {
3768                let source_path = match as_builtin {
3769                    true => None,
3770                    false => Some(
3771                        capability
3772                            .path
3773                            .clone()
3774                            .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3775                            .into(),
3776                    ),
3777                };
3778                out_capabilities.push(fdecl::Capability::Protocol(fdecl::Protocol {
3779                    name: Some(n.clone().into()),
3780                    source_path,
3781                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
3782                    delivery: capability.delivery.map(Into::into),
3783                    ..Default::default()
3784                }));
3785            }
3786        } else if let Some(n) = &capability.directory {
3787            let source_path = match as_builtin {
3788                true => None,
3789                false => {
3790                    Some(capability.path.as_ref().expect("missing source path").clone().into())
3791                }
3792            };
3793            let rights = extract_required_rights(capability, "capability")?;
3794            out_capabilities.push(fdecl::Capability::Directory(fdecl::Directory {
3795                name: Some(n.clone().into()),
3796                source_path,
3797                rights: Some(rights),
3798                ..Default::default()
3799            }));
3800        } else if let Some(n) = &capability.storage {
3801            if as_builtin {
3802                return Err(Error::internal(format!(
3803                    "built-in storage capabilities are not supported"
3804                )));
3805            }
3806            let backing_dir = capability
3807                .backing_dir
3808                .as_ref()
3809                .expect("storage has no path or backing_dir")
3810                .clone()
3811                .into();
3812
3813            let (source, _source_dictionary) = any_ref_to_decl_deprecated(
3814                options,
3815                capability.from.as_ref().unwrap().into(),
3816                None,
3817                None,
3818            );
3819            out_capabilities.push(fdecl::Capability::Storage(fdecl::Storage {
3820                name: Some(n.clone().into()),
3821                backing_dir: Some(backing_dir),
3822                subdir: capability.subdir.clone().map(Into::into),
3823                source: Some(source),
3824                storage_id: Some(
3825                    capability.storage_id.clone().expect("storage is missing storage_id").into(),
3826                ),
3827                ..Default::default()
3828            }));
3829        } else if let Some(n) = &capability.runner {
3830            let source_path = match as_builtin {
3831                true => None,
3832                false => {
3833                    Some(capability.path.as_ref().expect("missing source path").clone().into())
3834                }
3835            };
3836            out_capabilities.push(fdecl::Capability::Runner(fdecl::Runner {
3837                name: Some(n.clone().into()),
3838                source_path,
3839                ..Default::default()
3840            }));
3841        } else if let Some(n) = &capability.resolver {
3842            let source_path = match as_builtin {
3843                true => None,
3844                false => {
3845                    Some(capability.path.as_ref().expect("missing source path").clone().into())
3846                }
3847            };
3848            out_capabilities.push(fdecl::Capability::Resolver(fdecl::Resolver {
3849                name: Some(n.clone().into()),
3850                source_path,
3851                ..Default::default()
3852            }));
3853        } else if let Some(ns) = &capability.event_stream {
3854            if !as_builtin {
3855                return Err(Error::internal(format!(
3856                    "event_stream capabilities may only be declared as built-in capabilities"
3857                )));
3858            }
3859            for n in ns {
3860                out_capabilities.push(fdecl::Capability::EventStream(fdecl::EventStream {
3861                    name: Some(n.clone().into()),
3862                    ..Default::default()
3863                }));
3864            }
3865        } else if let Some(n) = &capability.dictionary {
3866            out_capabilities.push(fdecl::Capability::Dictionary(fdecl::Dictionary {
3867                name: Some(n.clone().into()),
3868                source_path: capability.path.clone().map(Into::into),
3869                ..Default::default()
3870            }));
3871        } else if let Some(c) = &capability.config {
3872            let value = configuration_to_value_deprecated(
3873                c,
3874                &capability,
3875                &capability.config_type,
3876                &capability.value,
3877            )?;
3878            out_capabilities.push(fdecl::Capability::Config(fdecl::Configuration {
3879                name: Some(c.clone().into()),
3880                value: Some(value),
3881                ..Default::default()
3882            }));
3883        } else {
3884            return Err(Error::internal(format!("no capability declaration recognized")));
3885        }
3886    }
3887    Ok(out_capabilities)
3888}
3889
3890pub fn translate_capabilities(
3891    options: &CompileOptions<'_>,
3892    capabilities_in: &Vec<ContextSpanned<ContextCapability>>,
3893    as_builtin: bool,
3894) -> Result<Vec<fdecl::Capability>, Error> {
3895    let mut out_capabilities = vec![];
3896    for cs_capability in capabilities_in {
3897        let capability = &cs_capability.value;
3898        if let Some(service) = &capability.service {
3899            for n in service.value.iter() {
3900                let source_path = match as_builtin {
3901                    true => None,
3902                    false => Some(
3903                        capability
3904                            .path
3905                            .clone()
3906                            .map(|s| s.value)
3907                            .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3908                            .into(),
3909                    ),
3910                };
3911                out_capabilities.push(fdecl::Capability::Service(fdecl::Service {
3912                    name: Some(n.clone().into()),
3913                    source_path,
3914                    ..Default::default()
3915                }));
3916            }
3917        } else if let Some(protocol) = &capability.protocol {
3918            for n in protocol.value.iter() {
3919                let source_path = match as_builtin {
3920                    true => None,
3921                    false => Some(
3922                        capability
3923                            .path
3924                            .clone()
3925                            .map(|s| s.value)
3926                            .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3927                            .into(),
3928                    ),
3929                };
3930                out_capabilities.push(fdecl::Capability::Protocol(fdecl::Protocol {
3931                    name: Some(n.clone().into()),
3932                    source_path,
3933                    #[cfg(fuchsia_api_level_at_least = "HEAD")]
3934                    delivery: capability.delivery.as_ref().map(|s| s.value.into()),
3935                    ..Default::default()
3936                }));
3937            }
3938        } else if let Some(n) = &capability.directory {
3939            let source_path = match as_builtin {
3940                true => None,
3941                false => Some(
3942                    capability.path.as_ref().expect("missing source path").value.clone().into(),
3943                ),
3944            };
3945            let rights = extract_required_rights(capability, "capability")?;
3946            out_capabilities.push(fdecl::Capability::Directory(fdecl::Directory {
3947                name: Some(n.value.clone().into()),
3948                source_path,
3949                rights: Some(rights),
3950                ..Default::default()
3951            }));
3952        } else if let Some(n) = &capability.storage {
3953            if as_builtin {
3954                return Err(Error::internal(format!(
3955                    "built-in storage capabilities are not supported"
3956                )));
3957            }
3958            let backing_dir = capability
3959                .backing_dir
3960                .as_ref()
3961                .expect("storage has no path or backing_dir")
3962                .value
3963                .clone()
3964                .into();
3965
3966            let (source, _source_dictionary) = any_ref_to_decl(
3967                options,
3968                (&capability.from.as_ref().unwrap().value).into(),
3969                None,
3970                None,
3971            );
3972            out_capabilities.push(fdecl::Capability::Storage(fdecl::Storage {
3973                name: Some(n.value.clone().into()),
3974                backing_dir: Some(backing_dir),
3975                subdir: capability.subdir.as_ref().map(|s| s.value.clone().into()),
3976                source: Some(source),
3977                storage_id: Some(
3978                    capability
3979                        .storage_id
3980                        .as_ref()
3981                        .expect("storage is missing storage_id")
3982                        .value
3983                        .clone()
3984                        .into(),
3985                ),
3986                ..Default::default()
3987            }));
3988        } else if let Some(n) = &capability.runner {
3989            let source_path = match as_builtin {
3990                true => None,
3991                false => Some(
3992                    capability.path.as_ref().expect("missing source path").value.clone().into(),
3993                ),
3994            };
3995            out_capabilities.push(fdecl::Capability::Runner(fdecl::Runner {
3996                name: Some(n.value.clone().into()),
3997                source_path,
3998                ..Default::default()
3999            }));
4000        } else if let Some(n) = &capability.resolver {
4001            let source_path = match as_builtin {
4002                true => None,
4003                false => Some(
4004                    capability.path.as_ref().expect("missing source path").value.clone().into(),
4005                ),
4006            };
4007            out_capabilities.push(fdecl::Capability::Resolver(fdecl::Resolver {
4008                name: Some(n.value.clone().into()),
4009                source_path,
4010                ..Default::default()
4011            }));
4012        } else if let Some(ns) = &capability.event_stream {
4013            if !as_builtin {
4014                return Err(Error::internal(format!(
4015                    "event_stream capabilities may only be declared as built-in capabilities"
4016                )));
4017            }
4018            for n in &ns.value {
4019                out_capabilities.push(fdecl::Capability::EventStream(fdecl::EventStream {
4020                    name: Some(n.clone().into()),
4021                    ..Default::default()
4022                }));
4023            }
4024        } else if let Some(n) = &capability.dictionary {
4025            out_capabilities.push(fdecl::Capability::Dictionary(fdecl::Dictionary {
4026                name: Some(n.value.clone().into()),
4027                source_path: capability.path.as_ref().map(|s| s.value.clone().into()),
4028                ..Default::default()
4029            }));
4030        } else if let Some(c) = &capability.config {
4031            let value = configuration_to_value(
4032                &c.value,
4033                &capability,
4034                &capability.config_type,
4035                &capability.value,
4036            )?;
4037            out_capabilities.push(fdecl::Capability::Config(fdecl::Configuration {
4038                name: Some(c.value.clone().into()),
4039                value: Some(value),
4040                ..Default::default()
4041            }));
4042        } else {
4043            return Err(Error::internal(format!("no capability declaration recognized")));
4044        }
4045    }
4046    Ok(out_capabilities)
4047}
4048
4049pub fn extract_required_rights<T>(in_obj: &T, keyword: &str) -> Result<fio::Operations, Error>
4050where
4051    T: RightsClause,
4052{
4053    match in_obj.rights() {
4054        Some(rights_tokens) => {
4055            let mut rights = Vec::new();
4056            for token in rights_tokens.0.iter() {
4057                rights.append(&mut token.expand())
4058            }
4059            if rights.is_empty() {
4060                return Err(Error::missing_rights(format!(
4061                    "Rights provided to `{}` are not well formed.",
4062                    keyword
4063                )));
4064            }
4065            let mut seen_rights = BTreeSet::new();
4066            let mut operations: fio::Operations = fio::Operations::empty();
4067            for right in rights.iter() {
4068                if seen_rights.contains(&right) {
4069                    return Err(Error::duplicate_rights(format!(
4070                        "Rights provided to `{}` are not well formed.",
4071                        keyword
4072                    )));
4073                }
4074                seen_rights.insert(right);
4075                operations |= *right;
4076            }
4077
4078            Ok(operations)
4079        }
4080        None => Err(Error::internal(format!(
4081            "No `{}` rights provided but required for directories",
4082            keyword
4083        ))),
4084    }
4085}
4086
4087/// Takes an `AnyRef` and returns the `fdecl::Ref` equivalent and the dictionary path, if
4088/// the ref was a dictionary ref.
4089pub fn any_ref_to_decl_deprecated(
4090    options: &CompileOptions<'_>,
4091    reference: AnyRef<'_>,
4092    all_capability_names: Option<&BTreeSet<&BorrowedName>>,
4093    all_collection_names: Option<&BTreeSet<&BorrowedName>>,
4094) -> (fdecl::Ref, Option<String>) {
4095    let ref_ = match reference {
4096        AnyRef::Named(name) => {
4097            if all_capability_names.is_some() && all_capability_names.unwrap().contains(name) {
4098                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4099            } else if all_collection_names.is_some() && all_collection_names.unwrap().contains(name)
4100            {
4101                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
4102            } else {
4103                fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
4104            }
4105        }
4106        AnyRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
4107        AnyRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
4108        AnyRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
4109        AnyRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
4110        AnyRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
4111        AnyRef::Dictionary(d) => {
4112            return dictionary_ref_to_source(&d);
4113        }
4114        AnyRef::OwnDictionary(name) => {
4115            fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4116        }
4117    };
4118    (ref_, None)
4119}
4120
4121/// Takes an `AnyRef` and returns the `fdecl::Ref` equivalent and the dictionary path, if
4122/// the ref was a dictionary ref.
4123pub fn any_ref_to_decl(
4124    options: &CompileOptions<'_>,
4125    reference: AnyRef<'_>,
4126    all_capability_names: Option<&BTreeSet<Name>>,
4127    all_collection_names: Option<&BTreeSet<Name>>,
4128) -> (fdecl::Ref, Option<String>) {
4129    let ref_ = match reference {
4130        AnyRef::Named(name) => {
4131            if all_capability_names.is_some()
4132                && all_capability_names.unwrap().contains::<BorrowedName>(name)
4133            {
4134                fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4135            } else if all_collection_names.is_some()
4136                && all_collection_names.unwrap().contains::<BorrowedName>(name)
4137            {
4138                fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
4139            } else {
4140                fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
4141            }
4142        }
4143        AnyRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
4144        AnyRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
4145        AnyRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
4146        AnyRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
4147        AnyRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
4148        AnyRef::Dictionary(d) => {
4149            return dictionary_ref_to_source(&d);
4150        }
4151        AnyRef::OwnDictionary(name) => {
4152            fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4153        }
4154    };
4155    (ref_, None)
4156}
4157
4158/// Takes a `DictionaryRef` and returns the `fdecl::Ref` equivalent and the dictionary path.
4159fn dictionary_ref_to_source(d: &DictionaryRef) -> (fdecl::Ref, Option<String>) {
4160    #[allow(unused)]
4161    let root = match &d.root {
4162        RootDictionaryRef::Named(name) => {
4163            fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
4164        }
4165        RootDictionaryRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
4166        RootDictionaryRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
4167    };
4168    (root, Some(d.path.to_string()))
4169}
4170
4171fn configuration_to_value_deprecated(
4172    name: &BorrowedName,
4173    capability: &Capability,
4174    config_type: &Option<ConfigType>,
4175    value: &Option<serde_json::Value>,
4176) -> Result<fdecl::ConfigValue, Error> {
4177    let Some(config_type) = config_type.as_ref() else {
4178        return Err(Error::InvalidArgs(format!(
4179            "Configuration field '{}' must have 'type' set",
4180            name
4181        )));
4182    };
4183    let Some(value) = value.as_ref() else {
4184        return Err(Error::InvalidArgs(format!(
4185            "Configuration field '{}' must have 'value' set",
4186            name
4187        )));
4188    };
4189
4190    let config_type = match config_type {
4191        ConfigType::Bool => cm_rust::ConfigValueType::Bool,
4192        ConfigType::Uint8 => cm_rust::ConfigValueType::Uint8,
4193        ConfigType::Uint16 => cm_rust::ConfigValueType::Uint16,
4194        ConfigType::Uint32 => cm_rust::ConfigValueType::Uint32,
4195        ConfigType::Uint64 => cm_rust::ConfigValueType::Uint64,
4196        ConfigType::Int8 => cm_rust::ConfigValueType::Int8,
4197        ConfigType::Int16 => cm_rust::ConfigValueType::Int16,
4198        ConfigType::Int32 => cm_rust::ConfigValueType::Int32,
4199        ConfigType::Int64 => cm_rust::ConfigValueType::Int64,
4200        ConfigType::String => {
4201            let Some(max_size) = capability.config_max_size else {
4202                return Err(Error::InvalidArgs(format!(
4203                    "Configuration field '{}' must have 'max_size' set",
4204                    name
4205                )));
4206            };
4207            cm_rust::ConfigValueType::String { max_size: max_size.into() }
4208        }
4209        ConfigType::Vector => {
4210            let Some(ref element) = capability.config_element_type else {
4211                return Err(Error::InvalidArgs(format!(
4212                    "Configuration field '{}' must have 'element_type' set",
4213                    name
4214                )));
4215            };
4216            let Some(max_count) = capability.config_max_count else {
4217                return Err(Error::InvalidArgs(format!(
4218                    "Configuration field '{}' must have 'max_count' set",
4219                    name
4220                )));
4221            };
4222            let nested_type = match element {
4223                ConfigNestedValueType::Bool { .. } => cm_rust::ConfigNestedValueType::Bool,
4224                ConfigNestedValueType::Uint8 { .. } => cm_rust::ConfigNestedValueType::Uint8,
4225                ConfigNestedValueType::Uint16 { .. } => cm_rust::ConfigNestedValueType::Uint16,
4226                ConfigNestedValueType::Uint32 { .. } => cm_rust::ConfigNestedValueType::Uint32,
4227                ConfigNestedValueType::Uint64 { .. } => cm_rust::ConfigNestedValueType::Uint64,
4228                ConfigNestedValueType::Int8 { .. } => cm_rust::ConfigNestedValueType::Int8,
4229                ConfigNestedValueType::Int16 { .. } => cm_rust::ConfigNestedValueType::Int16,
4230                ConfigNestedValueType::Int32 { .. } => cm_rust::ConfigNestedValueType::Int32,
4231                ConfigNestedValueType::Int64 { .. } => cm_rust::ConfigNestedValueType::Int64,
4232                ConfigNestedValueType::String { max_size } => {
4233                    cm_rust::ConfigNestedValueType::String { max_size: (*max_size).into() }
4234                }
4235            };
4236            cm_rust::ConfigValueType::Vector { max_count: max_count.into(), nested_type }
4237        }
4238    };
4239    let value = config_value_file::field::config_value_from_json_value(value, &config_type)
4240        .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", name, e)))?;
4241    Ok(value.native_into_fidl())
4242}
4243
4244fn configuration_to_value(
4245    name: &BorrowedName,
4246    capability: &ContextCapability,
4247    config_type: &Option<ContextSpanned<ConfigType>>,
4248    value: &Option<ContextSpanned<serde_json::Value>>,
4249) -> Result<fdecl::ConfigValue, Error> {
4250    let Some(config_type) = config_type.as_ref() else {
4251        return Err(Error::InvalidArgs(format!(
4252            "Configuration field '{}' must have 'type' set",
4253            name
4254        )));
4255    };
4256    let Some(value) = value.as_ref() else {
4257        return Err(Error::InvalidArgs(format!(
4258            "Configuration field '{}' must have 'value' set",
4259            name
4260        )));
4261    };
4262
4263    let config_type = match config_type.value {
4264        ConfigType::Bool => cm_rust::ConfigValueType::Bool,
4265        ConfigType::Uint8 => cm_rust::ConfigValueType::Uint8,
4266        ConfigType::Uint16 => cm_rust::ConfigValueType::Uint16,
4267        ConfigType::Uint32 => cm_rust::ConfigValueType::Uint32,
4268        ConfigType::Uint64 => cm_rust::ConfigValueType::Uint64,
4269        ConfigType::Int8 => cm_rust::ConfigValueType::Int8,
4270        ConfigType::Int16 => cm_rust::ConfigValueType::Int16,
4271        ConfigType::Int32 => cm_rust::ConfigValueType::Int32,
4272        ConfigType::Int64 => cm_rust::ConfigValueType::Int64,
4273        ConfigType::String => {
4274            let Some(max_size) = capability.config_max_size.as_ref() else {
4275                return Err(Error::InvalidArgs(format!(
4276                    "Configuration field '{}' must have 'max_size' set",
4277                    name
4278                )));
4279            };
4280            let size_val: u32 = max_size.value.get();
4281            cm_rust::ConfigValueType::String { max_size: size_val }
4282        }
4283        ConfigType::Vector => {
4284            let Some(ref element) = capability.config_element_type else {
4285                return Err(Error::InvalidArgs(format!(
4286                    "Configuration field '{}' must have 'element_type' set",
4287                    name
4288                )));
4289            };
4290            let Some(max_count) = capability.config_max_count.as_ref() else {
4291                return Err(Error::InvalidArgs(format!(
4292                    "Configuration field '{}' must have 'max_count' set",
4293                    name
4294                )));
4295            };
4296            let max_count_val = max_count.value.get();
4297            let nested_type = match element.value {
4298                ConfigNestedValueType::Bool { .. } => cm_rust::ConfigNestedValueType::Bool,
4299                ConfigNestedValueType::Uint8 { .. } => cm_rust::ConfigNestedValueType::Uint8,
4300                ConfigNestedValueType::Uint16 { .. } => cm_rust::ConfigNestedValueType::Uint16,
4301                ConfigNestedValueType::Uint32 { .. } => cm_rust::ConfigNestedValueType::Uint32,
4302                ConfigNestedValueType::Uint64 { .. } => cm_rust::ConfigNestedValueType::Uint64,
4303                ConfigNestedValueType::Int8 { .. } => cm_rust::ConfigNestedValueType::Int8,
4304                ConfigNestedValueType::Int16 { .. } => cm_rust::ConfigNestedValueType::Int16,
4305                ConfigNestedValueType::Int32 { .. } => cm_rust::ConfigNestedValueType::Int32,
4306                ConfigNestedValueType::Int64 { .. } => cm_rust::ConfigNestedValueType::Int64,
4307                ConfigNestedValueType::String { max_size } => {
4308                    cm_rust::ConfigNestedValueType::String { max_size: max_size.get().into() }
4309                }
4310            };
4311            cm_rust::ConfigValueType::Vector { max_count: max_count_val.into(), nested_type }
4312        }
4313    };
4314    let value = config_value_file::field::config_value_from_json_value(&value.value, &config_type)
4315        .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", name, e)))?;
4316    Ok(value.native_into_fidl())
4317}
4318
4319#[cfg(test)]
4320pub mod test_util {
4321    /// Construct a CML [Document] from the provided JSON literal expression or panic if error.
4322    macro_rules! must_parse_cml {
4323        ($($input:tt)+) => {
4324            serde_json::from_str::<Document>(&json!($($input)+).to_string())
4325                .expect("deserialization failed")
4326        };
4327    }
4328    pub(crate) use must_parse_cml;
4329}
4330
4331#[cfg(test)]
4332mod tests {
4333    use super::*;
4334    use crate::error::Error;
4335    use crate::features::Feature;
4336    use crate::translate::test_util::must_parse_cml;
4337    use crate::types::offer::create_offer;
4338    use crate::{
4339        AsClause, CapabilityClause, Document, FromClause, OneOrMany, Path, PathClause, Program,
4340        load_cml_with_context,
4341    };
4342    use assert_matches::assert_matches;
4343    use cm_fidl_validator::error::{AvailabilityList, DeclField, Error as CmFidlError, ErrorList};
4344    use cm_types::{self as cm, Name};
4345    use difference::Changeset;
4346    use serde_json::{Map, Value, json};
4347    use std::collections::BTreeSet;
4348    use std::convert::Into;
4349    use std::str::FromStr;
4350    use {
4351        fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
4352    };
4353
4354    macro_rules! test_compile_context {
4355        (
4356            $(
4357                $(#[$m:meta])*
4358                $test_name:ident => {
4359                    $(features = $features:expr,)?
4360                    input = $input:expr,
4361                    output = $expected:expr,
4362                },
4363            )+
4364        ) => {
4365            $(
4366                $(#[$m])*
4367                #[test]
4368                fn $test_name() {
4369                    let fake_file = std::path::Path::new("test.cml");
4370                    let input_str = serde_json::to_string(&$input).expect("failed to serialize input json");
4371                    let document = crate::load_cml_with_context(&input_str, fake_file).expect("should work");
4372
4373                    let options = CompileOptions::new()
4374                        .file(&fake_file)
4375                        .config_package_path("fake.cvf");
4376
4377                    $(
4378                        let features = $features;
4379                        let options = options.features(&features);
4380                    )?
4381
4382                    let actual_context = compile_context(&document, options).expect("compilation failed");
4383
4384                    if actual_context != $expected {
4385                        let e = format!("{:#?}", $expected);
4386                        let a = format!("{:#?}", actual_context);
4387                        panic!("Test {} failed comparison:\n{}", stringify!($test_name), Changeset::new(&a, &e, "\n"));
4388                    }
4389
4390                    let input = serde_json::from_str(&$input.to_string()).expect("deserialization failed");
4391                    let options = CompileOptions::new().config_package_path("fake.cvf");
4392                    // Optionally specify features.
4393                    $(let features = $features; let options = options.features(&features);)?
4394                    let actual_deprecated = compile(&input, options).expect("compilation failed");
4395
4396                    if actual_deprecated != $expected {
4397                        let e = format!("{:#?}", $expected);
4398                        let a = format!("{:#?}", actual_deprecated);
4399                        panic!("{}", Changeset::new(&a, &e, "\n"));
4400                    }
4401                }
4402            )+
4403        };
4404    }
4405
4406    fn default_component_decl() -> fdecl::Component {
4407        fdecl::Component::default()
4408    }
4409
4410    test_compile_context! {
4411        test_compile_empty_dep => {
4412            input = json!({}),
4413            output = default_component_decl(),
4414        },
4415
4416        test_compile_empty_includes => {
4417            input = json!({ "include": [] }),
4418            output = default_component_decl(),
4419        },
4420
4421        test_compile_offer_to_all_and_diff_sources => {
4422            input = json!({
4423                "children": [
4424                    {
4425                        "name": "logger",
4426                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4427                    },
4428                ],
4429                "collections": [
4430                    {
4431                        "name": "coll",
4432                        "durability": "transient",
4433                    },
4434                ],
4435                "offer": [
4436                    {
4437                        "protocol": "fuchsia.logger.LogSink",
4438                        "from": "parent",
4439                        "to": "all",
4440                    },
4441                    {
4442                        "protocol": "fuchsia.logger.LogSink",
4443                        "from": "framework",
4444                        "to": "#logger",
4445                        "as": "LogSink2",
4446                    },
4447                ],
4448            }),
4449            output = fdecl::Component {
4450                offers: Some(vec![
4451                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4452                        source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4453                        source_name: Some("fuchsia.logger.LogSink".into()),
4454                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4455                            name: "logger".into(),
4456                            collection: None,
4457                        })),
4458                        target_name: Some("LogSink2".into()),
4459                        dependency_type: Some(fdecl::DependencyType::Strong),
4460                        availability: Some(fdecl::Availability::Required),
4461                        ..Default::default()
4462                    }),
4463                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4464                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4465                        source_name: Some("fuchsia.logger.LogSink".into()),
4466                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4467                            name: "logger".into(),
4468                            collection: None,
4469                        })),
4470                        target_name: Some("fuchsia.logger.LogSink".into()),
4471                        dependency_type: Some(fdecl::DependencyType::Strong),
4472                        availability: Some(fdecl::Availability::Required),
4473                        ..Default::default()
4474                    }),
4475                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4476                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4477                        source_name: Some("fuchsia.logger.LogSink".into()),
4478                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4479                            name: "coll".into(),
4480                        })),
4481                        target_name: Some("fuchsia.logger.LogSink".into()),
4482                        dependency_type: Some(fdecl::DependencyType::Strong),
4483                        availability: Some(fdecl::Availability::Required),
4484                        ..Default::default()
4485                    }),
4486                ]),
4487                children: Some(vec![fdecl::Child {
4488                    name: Some("logger".into()),
4489                    url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4490                    startup: Some(fdecl::StartupMode::Lazy),
4491                    ..Default::default()
4492                }]),
4493                collections: Some(vec![fdecl::Collection {
4494                    name: Some("coll".into()),
4495                    durability: Some(fdecl::Durability::Transient),
4496                    ..Default::default()
4497                }]),
4498                ..default_component_decl()
4499            },
4500        },
4501
4502        test_compile_offer_to_all => {
4503            input = json!({
4504                "children": [
4505                    {
4506                        "name": "logger",
4507                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4508                    },
4509                    {
4510                        "name": "something",
4511                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4512                    },
4513                ],
4514                "collections": [
4515                    {
4516                        "name": "coll",
4517                        "durability": "transient",
4518                    },
4519                ],
4520                "offer": [
4521                    {
4522                        "protocol": "fuchsia.logger.LogSink",
4523                        "from": "parent",
4524                        "to": "all",
4525                    },
4526                    {
4527                        "protocol": "fuchsia.inspect.InspectSink",
4528                        "from": "parent",
4529                        "to": "all",
4530                    },
4531                    {
4532                        "protocol": "fuchsia.logger.LegacyLog",
4533                        "from": "parent",
4534                        "to": "#logger",
4535                    },
4536                ],
4537            }),
4538            output = fdecl::Component {
4539                offers: Some(vec![
4540                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4541                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4542                        source_name: Some("fuchsia.logger.LegacyLog".into()),
4543                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4544                            name: "logger".into(),
4545                            collection: None,
4546                        })),
4547                        target_name: Some("fuchsia.logger.LegacyLog".into()),
4548                        dependency_type: Some(fdecl::DependencyType::Strong),
4549                        availability: Some(fdecl::Availability::Required),
4550                        ..Default::default()
4551                    }),
4552                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4553                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4554                        source_name: Some("fuchsia.logger.LogSink".into()),
4555                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4556                            name: "logger".into(),
4557                            collection: None,
4558                        })),
4559                        target_name: Some("fuchsia.logger.LogSink".into()),
4560                        dependency_type: Some(fdecl::DependencyType::Strong),
4561                        availability: Some(fdecl::Availability::Required),
4562                        ..Default::default()
4563                    }),
4564                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4565                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4566                        source_name: Some("fuchsia.logger.LogSink".into()),
4567                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4568                            name: "something".into(),
4569                            collection: None,
4570                        })),
4571                        target_name: Some("fuchsia.logger.LogSink".into()),
4572                        dependency_type: Some(fdecl::DependencyType::Strong),
4573                        availability: Some(fdecl::Availability::Required),
4574                        ..Default::default()
4575                    }),
4576                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4577                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4578                        source_name: Some("fuchsia.logger.LogSink".into()),
4579                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4580                            name: "coll".into(),
4581                        })),
4582                        target_name: Some("fuchsia.logger.LogSink".into()),
4583                        dependency_type: Some(fdecl::DependencyType::Strong),
4584                        availability: Some(fdecl::Availability::Required),
4585                        ..Default::default()
4586                    }),
4587                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4588                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4589                        source_name: Some("fuchsia.inspect.InspectSink".into()),
4590                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4591                            name: "logger".into(),
4592                            collection: None,
4593                        })),
4594                        target_name: Some("fuchsia.inspect.InspectSink".into()),
4595                        dependency_type: Some(fdecl::DependencyType::Strong),
4596                        availability: Some(fdecl::Availability::Required),
4597                        ..Default::default()
4598                    }),
4599                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4600                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4601                        source_name: Some("fuchsia.inspect.InspectSink".into()),
4602                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4603                            name: "something".into(),
4604                            collection: None,
4605                        })),
4606                        target_name: Some("fuchsia.inspect.InspectSink".into()),
4607                        dependency_type: Some(fdecl::DependencyType::Strong),
4608                        availability: Some(fdecl::Availability::Required),
4609                        ..Default::default()
4610                    }),
4611                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4612                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4613                        source_name: Some("fuchsia.inspect.InspectSink".into()),
4614                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4615                            name: "coll".into(),
4616                        })),
4617                        target_name: Some("fuchsia.inspect.InspectSink".into()),
4618                        dependency_type: Some(fdecl::DependencyType::Strong),
4619                        availability: Some(fdecl::Availability::Required),
4620                        ..Default::default()
4621                    }),
4622                ]),
4623                children: Some(vec![
4624                    fdecl::Child {
4625                        name: Some("logger".into()),
4626                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4627                        startup: Some(fdecl::StartupMode::Lazy),
4628                        ..Default::default()
4629                    },
4630                    fdecl::Child {
4631                        name: Some("something".into()),
4632                        url: Some(
4633                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4634                        ),
4635                        startup: Some(fdecl::StartupMode::Lazy),
4636                        ..Default::default()
4637                    },
4638                ]),
4639                collections: Some(vec![fdecl::Collection {
4640                    name: Some("coll".into()),
4641                    durability: Some(fdecl::Durability::Transient),
4642                    ..Default::default()
4643                }]),
4644                ..default_component_decl()
4645            },
4646        },
4647
4648        test_compile_offer_to_all_hides_individual_duplicate_routes => {
4649            input = json!({
4650                "children": [
4651                    {
4652                        "name": "logger",
4653                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4654                    },
4655                    {
4656                        "name": "something",
4657                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4658                    },
4659                    {
4660                        "name": "something-v2",
4661                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
4662                    },
4663                ],
4664                "collections": [
4665                    {
4666                        "name": "coll",
4667                        "durability": "transient",
4668                    },
4669                    {
4670                        "name": "coll2",
4671                        "durability": "transient",
4672                    },
4673                ],
4674                "offer": [
4675                    {
4676                        "protocol": "fuchsia.logger.LogSink",
4677                        "from": "parent",
4678                        "to": "#logger",
4679                    },
4680                    {
4681                        "protocol": "fuchsia.logger.LogSink",
4682                        "from": "parent",
4683                        "to": "all",
4684                    },
4685                    {
4686                        "protocol": "fuchsia.logger.LogSink",
4687                        "from": "parent",
4688                        "to": [ "#something", "#something-v2", "#coll2"],
4689                    },
4690                    {
4691                        "protocol": "fuchsia.logger.LogSink",
4692                        "from": "parent",
4693                        "to": "#coll",
4694                    },
4695                ],
4696            }),
4697            output = fdecl::Component {
4698                offers: Some(vec![
4699                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4700                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4701                        source_name: Some("fuchsia.logger.LogSink".into()),
4702                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4703                            name: "logger".into(),
4704                            collection: None,
4705                        })),
4706                        target_name: Some("fuchsia.logger.LogSink".into()),
4707                        dependency_type: Some(fdecl::DependencyType::Strong),
4708                        availability: Some(fdecl::Availability::Required),
4709                        ..Default::default()
4710                    }),
4711                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4712                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4713                        source_name: Some("fuchsia.logger.LogSink".into()),
4714                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4715                            name: "something".into(),
4716                            collection: None,
4717                        })),
4718                        target_name: Some("fuchsia.logger.LogSink".into()),
4719                        dependency_type: Some(fdecl::DependencyType::Strong),
4720                        availability: Some(fdecl::Availability::Required),
4721                        ..Default::default()
4722                    }),
4723                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4724                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4725                        source_name: Some("fuchsia.logger.LogSink".into()),
4726                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4727                            name: "something-v2".into(),
4728                            collection: None,
4729                        })),
4730                        target_name: Some("fuchsia.logger.LogSink".into()),
4731                        dependency_type: Some(fdecl::DependencyType::Strong),
4732                        availability: Some(fdecl::Availability::Required),
4733                        ..Default::default()
4734                    }),
4735                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4736                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4737                        source_name: Some("fuchsia.logger.LogSink".into()),
4738                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4739                            name: "coll2".into(),
4740                        })),
4741                        target_name: Some("fuchsia.logger.LogSink".into()),
4742                        dependency_type: Some(fdecl::DependencyType::Strong),
4743                        availability: Some(fdecl::Availability::Required),
4744                        ..Default::default()
4745                    }),
4746                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4747                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4748                        source_name: Some("fuchsia.logger.LogSink".into()),
4749                        target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4750                            name: "coll".into(),
4751                        })),
4752                        target_name: Some("fuchsia.logger.LogSink".into()),
4753                        dependency_type: Some(fdecl::DependencyType::Strong),
4754                        availability: Some(fdecl::Availability::Required),
4755                        ..Default::default()
4756                    }),
4757                ]),
4758                children: Some(vec![
4759                    fdecl::Child {
4760                        name: Some("logger".into()),
4761                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4762                        startup: Some(fdecl::StartupMode::Lazy),
4763                        ..Default::default()
4764                    },
4765                    fdecl::Child {
4766                        name: Some("something".into()),
4767                        url: Some(
4768                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4769                        ),
4770                        startup: Some(fdecl::StartupMode::Lazy),
4771                        ..Default::default()
4772                    },
4773                    fdecl::Child {
4774                        name: Some("something-v2".into()),
4775                        url: Some(
4776                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
4777                        ),
4778                        startup: Some(fdecl::StartupMode::Lazy),
4779                        ..Default::default()
4780                    },
4781                ]),
4782                collections: Some(vec![fdecl::Collection {
4783                    name: Some("coll".into()),
4784                    durability: Some(fdecl::Durability::Transient),
4785                    ..Default::default()
4786                }, fdecl::Collection {
4787                    name: Some("coll2".into()),
4788                    durability: Some(fdecl::Durability::Transient),
4789                    ..Default::default()
4790                }]),
4791                ..default_component_decl()
4792            },
4793        },
4794
4795        test_compile_offer_to_all_from_child => {
4796            input = json!({
4797                "children": [
4798                    {
4799                        "name": "logger",
4800                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4801                    },
4802                    {
4803                        "name": "something",
4804                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4805                    },
4806                    {
4807                        "name": "something-v2",
4808                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
4809                    },
4810                ],
4811                "offer": [
4812                    {
4813                        "protocol": "fuchsia.logger.LogSink",
4814                        "from": "#logger",
4815                        "to": "all",
4816                    },
4817                ],
4818            }),
4819            output = fdecl::Component {
4820                offers: Some(vec![
4821                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4822                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4823                            name: "logger".into(),
4824                            collection: None,
4825                        })),
4826                        source_name: Some("fuchsia.logger.LogSink".into()),
4827                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4828                            name: "something".into(),
4829                            collection: None,
4830                        })),
4831                        target_name: Some("fuchsia.logger.LogSink".into()),
4832                        dependency_type: Some(fdecl::DependencyType::Strong),
4833                        availability: Some(fdecl::Availability::Required),
4834                        ..Default::default()
4835                    }),
4836                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4837                        source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4838                            name: "logger".into(),
4839                            collection: None,
4840                        })),
4841                        source_name: Some("fuchsia.logger.LogSink".into()),
4842                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4843                            name: "something-v2".into(),
4844                            collection: None,
4845                        })),
4846                        target_name: Some("fuchsia.logger.LogSink".into()),
4847                        dependency_type: Some(fdecl::DependencyType::Strong),
4848                        availability: Some(fdecl::Availability::Required),
4849                        ..Default::default()
4850                    }),
4851                ]),
4852                children: Some(vec![
4853                    fdecl::Child {
4854                        name: Some("logger".into()),
4855                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4856                        startup: Some(fdecl::StartupMode::Lazy),
4857                        ..Default::default()
4858                    },
4859                    fdecl::Child {
4860                        name: Some("something".into()),
4861                        url: Some(
4862                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4863                        ),
4864                        startup: Some(fdecl::StartupMode::Lazy),
4865                        ..Default::default()
4866                    },
4867                    fdecl::Child {
4868                        name: Some("something-v2".into()),
4869                        url: Some(
4870                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
4871                        ),
4872                        startup: Some(fdecl::StartupMode::Lazy),
4873                        ..Default::default()
4874                    },
4875                ]),
4876                ..default_component_decl()
4877            },
4878        },
4879
4880        test_compile_offer_multiple_protocols_to_single_array_syntax_and_all => {
4881            input = json!({
4882                "children": [
4883                    {
4884                        "name": "something",
4885                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4886                    },
4887                ],
4888                "offer": [
4889                    {
4890                        "protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
4891                        "from": "parent",
4892                        "to": "#something",
4893                    },
4894                    {
4895                        "protocol": "fuchsia.logger.LogSink",
4896                        "from": "parent",
4897                        "to": "all",
4898                    },
4899                ],
4900            }),
4901            output = fdecl::Component {
4902                offers: Some(vec![
4903                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4904                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4905                        source_name: Some("fuchsia.logger.LogSink".into()),
4906                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4907                            name: "something".into(),
4908                            collection: None,
4909                        })),
4910                        target_name: Some("fuchsia.logger.LogSink".into()),
4911                        dependency_type: Some(fdecl::DependencyType::Strong),
4912                        availability: Some(fdecl::Availability::Required),
4913                        ..Default::default()
4914                    }),
4915                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4916                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4917                        source_name: Some("fuchsia.inspect.InspectSink".into()),
4918                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4919                            name: "something".into(),
4920                            collection: None,
4921                        })),
4922                        target_name: Some("fuchsia.inspect.InspectSink".into()),
4923                        dependency_type: Some(fdecl::DependencyType::Strong),
4924                        availability: Some(fdecl::Availability::Required),
4925                        ..Default::default()
4926                    }),
4927                ]),
4928                children: Some(vec![
4929                    fdecl::Child {
4930                        name: Some("something".into()),
4931                        url: Some(
4932                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4933                        ),
4934                        startup: Some(fdecl::StartupMode::Lazy),
4935                        ..Default::default()
4936                    },
4937                ]),
4938                ..default_component_decl()
4939            },
4940        },
4941
4942        test_compile_offer_to_all_array_and_single => {
4943            input = json!({
4944                "children": [
4945                    {
4946                        "name": "something",
4947                        "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4948                    },
4949                ],
4950                "offer": [
4951                    {
4952                        "protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
4953                        "from": "parent",
4954                        "to": "all",
4955                    },
4956                    {
4957                        "protocol": "fuchsia.logger.LogSink",
4958                        "from": "parent",
4959                        "to": "#something",
4960                    },
4961                ],
4962            }),
4963            output = fdecl::Component {
4964                offers: Some(vec![
4965                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4966                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4967                        source_name: Some("fuchsia.logger.LogSink".into()),
4968                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4969                            name: "something".into(),
4970                            collection: None,
4971                        })),
4972                        target_name: Some("fuchsia.logger.LogSink".into()),
4973                        dependency_type: Some(fdecl::DependencyType::Strong),
4974                        availability: Some(fdecl::Availability::Required),
4975                        ..Default::default()
4976                    }),
4977                    fdecl::Offer::Protocol(fdecl::OfferProtocol {
4978                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4979                        source_name: Some("fuchsia.inspect.InspectSink".into()),
4980                        target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4981                            name: "something".into(),
4982                            collection: None,
4983                        })),
4984                        target_name: Some("fuchsia.inspect.InspectSink".into()),
4985                        dependency_type: Some(fdecl::DependencyType::Strong),
4986                        availability: Some(fdecl::Availability::Required),
4987                        ..Default::default()
4988                    }),
4989                ]),
4990                children: Some(vec![
4991                    fdecl::Child {
4992                        name: Some("something".into()),
4993                        url: Some(
4994                            "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4995                        ),
4996                        startup: Some(fdecl::StartupMode::Lazy),
4997                        ..Default::default()
4998                    },
4999                ]),
5000                ..default_component_decl()
5001            },
5002        },
5003
5004        test_compile_program => {
5005            input = json!({
5006                "program": {
5007                    "runner": "elf",
5008                    "binary": "bin/app",
5009                },
5010            }),
5011            output = fdecl::Component {
5012                program: Some(fdecl::Program {
5013                    runner: Some("elf".to_string()),
5014                    info: Some(fdata::Dictionary {
5015                        entries: Some(vec![fdata::DictionaryEntry {
5016                            key: "binary".to_string(),
5017                            value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5018                        }]),
5019                        ..Default::default()
5020                    }),
5021                    ..Default::default()
5022                }),
5023                ..default_component_decl()
5024            },
5025        },
5026
5027        test_compile_program_with_use_runner => {
5028            input = json!({
5029                "program": {
5030                    "binary": "bin/app",
5031                },
5032                "use": [
5033                    { "runner": "elf", "from": "parent", },
5034                ],
5035            }),
5036            output = fdecl::Component {
5037                program: Some(fdecl::Program {
5038                    runner: None,
5039                    info: Some(fdata::Dictionary {
5040                        entries: Some(vec![fdata::DictionaryEntry {
5041                            key: "binary".to_string(),
5042                            value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5043                        }]),
5044                        ..Default::default()
5045                    }),
5046                    ..Default::default()
5047                }),
5048                uses: Some(vec![
5049                    fdecl::Use::Runner (
5050                        fdecl::UseRunner {
5051                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5052                            source_name: Some("elf".to_string()),
5053                            ..Default::default()
5054                        }
5055                    ),
5056                ]),
5057                ..default_component_decl()
5058            },
5059        },
5060
5061        test_compile_program_with_nested_objects => {
5062            input = json!({
5063                "program": {
5064                    "runner": "elf",
5065                    "binary": "bin/app",
5066                    "one": {
5067                        "two": {
5068                            "three.four": {
5069                                "five": "six"
5070                            }
5071                        },
5072                    }
5073                },
5074            }),
5075            output = fdecl::Component {
5076                program: Some(fdecl::Program {
5077                    runner: Some("elf".to_string()),
5078                    info: Some(fdata::Dictionary {
5079                        entries: Some(vec![
5080                            fdata::DictionaryEntry {
5081                                key: "binary".to_string(),
5082                                value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5083                            },
5084                            fdata::DictionaryEntry {
5085                                key: "one.two.three.four.five".to_string(),
5086                                value: Some(Box::new(fdata::DictionaryValue::Str("six".to_string()))),
5087                            },
5088                        ]),
5089                        ..Default::default()
5090                    }),
5091                    ..Default::default()
5092                }),
5093                ..default_component_decl()
5094            },
5095        },
5096
5097        test_compile_program_with_array_of_objects => {
5098            input = json!({
5099                "program": {
5100                    "runner": "elf",
5101                    "binary": "bin/app",
5102                    "networks": [
5103                        {
5104                            "endpoints": [
5105                                {
5106                                    "name": "device",
5107                                    "mac": "aa:bb:cc:dd:ee:ff"
5108                                },
5109                                {
5110                                    "name": "emu",
5111                                    "mac": "ff:ee:dd:cc:bb:aa"
5112                                },
5113                            ],
5114                            "name": "external_network"
5115                        }
5116                    ],
5117                },
5118            }),
5119            output = fdecl::Component {
5120                program: Some(fdecl::Program {
5121                    runner: Some("elf".to_string()),
5122                    info: Some(fdata::Dictionary {
5123                        entries: Some(vec![
5124                            fdata::DictionaryEntry {
5125                                key: "binary".to_string(),
5126                                value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5127                            },
5128                            fdata::DictionaryEntry {
5129                                key: "networks".to_string(),
5130                                value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
5131                                    fdata::Dictionary {
5132                                        entries: Some(vec![
5133                                            fdata::DictionaryEntry {
5134                                                key: "endpoints".to_string(),
5135                                                value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
5136                                                    fdata::Dictionary {
5137                                                        entries: Some(vec![
5138                                                            fdata::DictionaryEntry {
5139                                                                key: "mac".to_string(),
5140                                                                value: Some(Box::new(fdata::DictionaryValue::Str("aa:bb:cc:dd:ee:ff".to_string()))),
5141                                                            },
5142                                                            fdata::DictionaryEntry {
5143                                                                key: "name".to_string(),
5144                                                                value: Some(Box::new(fdata::DictionaryValue::Str("device".to_string()))),
5145                                                            }
5146                                                        ]),
5147                                                        ..Default::default()
5148                                                    },
5149                                                    fdata::Dictionary {
5150                                                        entries: Some(vec![
5151                                                            fdata::DictionaryEntry {
5152                                                                key: "mac".to_string(),
5153                                                                value: Some(Box::new(fdata::DictionaryValue::Str("ff:ee:dd:cc:bb:aa".to_string()))),
5154                                                            },
5155                                                            fdata::DictionaryEntry {
5156                                                                key: "name".to_string(),
5157                                                                value: Some(Box::new(fdata::DictionaryValue::Str("emu".to_string()))),
5158                                                            }
5159                                                        ]),
5160                                                        ..Default::default()
5161                                                    },
5162                                                ])))
5163                                            },
5164                                            fdata::DictionaryEntry {
5165                                                key: "name".to_string(),
5166                                                value: Some(Box::new(fdata::DictionaryValue::Str("external_network".to_string()))),
5167                                            },
5168                                        ]),
5169                                        ..Default::default()
5170                                    }
5171                                ]))),
5172                            },
5173                        ]),
5174                        ..Default::default()
5175                    }),
5176                    ..Default::default()
5177                }),
5178                ..default_component_decl()
5179            },
5180        },
5181
5182        test_compile_use => {
5183            features = FeatureSet::from(vec![Feature::UseDictionaries]),
5184            input = json!({
5185                "use": [
5186                    {
5187                        "protocol": "LegacyCoolFonts",
5188                        "path": "/svc/fuchsia.fonts.LegacyProvider",
5189                        "availability": "optional",
5190                    },
5191                    {
5192                        "protocol": "LegacyCoolFonts",
5193                        "numbered_handle": 0xab,
5194                    },
5195                    { "protocol": "fuchsia.sys2.LegacyRealm", "from": "framework" },
5196                    { "protocol": "fuchsia.sys2.StorageAdmin", "from": "#data-storage" },
5197                    { "protocol": "fuchsia.sys2.DebugProto", "from": "debug" },
5198                    { "protocol": "fuchsia.sys2.DictionaryProto", "from": "#logger/in/dict" },
5199                    { "protocol": "fuchsia.sys2.Echo", "from": "self", "availability": "transitional" },
5200                    { "service": "fuchsia.sys2.EchoService", "from": "parent/dict", },
5201                    { "directory": "assets", "rights" : ["read_bytes"], "path": "/data/assets" },
5202                    {
5203                        "directory": "config",
5204                        "path": "/data/config",
5205                        "from": "parent",
5206                        "rights": ["read_bytes"],
5207                        "subdir": "fonts",
5208                    },
5209                    { "storage": "hippos", "path": "/hippos" },
5210                    { "storage": "cache", "path": "/tmp" },
5211                    {
5212                        "event_stream": "bar_stream",
5213                    },
5214                    {
5215                        "event_stream": ["foobar", "stream"],
5216                        "scope": ["#logger", "#modular"],
5217                        "path": "/event_stream/another",
5218                    },
5219                    { "runner": "usain", "from": "parent", },
5220                    {
5221                        "dictionary": "toolbox",
5222                        "path": "/svc",
5223                    },
5224                ],
5225                "capabilities": [
5226                    { "protocol": "fuchsia.sys2.Echo" },
5227                    {
5228                        "config": "fuchsia.config.Config",
5229                        "type": "bool",
5230                        "value": true,
5231                    },
5232                    {
5233                        "storage": "data-storage",
5234                        "from": "parent",
5235                        "backing_dir": "minfs",
5236                        "storage_id": "static_instance_id_or_moniker",
5237                    }
5238                ],
5239                "children": [
5240                    {
5241                        "name": "logger",
5242                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
5243                        "environment": "#env_one"
5244                    }
5245                ],
5246                "collections": [
5247                    {
5248                        "name": "modular",
5249                        "durability": "transient",
5250                    },
5251                ],
5252                "environments": [
5253                    {
5254                        "name": "env_one",
5255                        "extends": "realm",
5256                    }
5257                ]
5258            }),
5259            output = fdecl::Component {
5260                uses: Some(vec![
5261                    fdecl::Use::Protocol (
5262                        fdecl::UseProtocol {
5263                            dependency_type: Some(fdecl::DependencyType::Strong),
5264                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5265                            source_name: Some("LegacyCoolFonts".to_string()),
5266                            target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
5267                            availability: Some(fdecl::Availability::Optional),
5268                            ..Default::default()
5269                        }
5270                    ),
5271                    fdecl::Use::Protocol (
5272                        fdecl::UseProtocol {
5273                            dependency_type: Some(fdecl::DependencyType::Strong),
5274                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5275                            source_name: Some("LegacyCoolFonts".to_string()),
5276                            numbered_handle: Some(0xab),
5277                            availability: Some(fdecl::Availability::Required),
5278                            ..Default::default()
5279                        }
5280                    ),
5281                    fdecl::Use::Protocol (
5282                        fdecl::UseProtocol {
5283                            dependency_type: Some(fdecl::DependencyType::Strong),
5284                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
5285                            source_name: Some("fuchsia.sys2.LegacyRealm".to_string()),
5286                            target_path: Some("/svc/fuchsia.sys2.LegacyRealm".to_string()),
5287                            availability: Some(fdecl::Availability::Required),
5288                            ..Default::default()
5289                        }
5290                    ),
5291                    fdecl::Use::Protocol (
5292                        fdecl::UseProtocol {
5293                            dependency_type: Some(fdecl::DependencyType::Strong),
5294                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data-storage".to_string() })),
5295                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5296                            target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
5297                            availability: Some(fdecl::Availability::Required),
5298                            ..Default::default()
5299                        }
5300                    ),
5301                    fdecl::Use::Protocol (
5302                        fdecl::UseProtocol {
5303                            dependency_type: Some(fdecl::DependencyType::Strong),
5304                            source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
5305                            source_name: Some("fuchsia.sys2.DebugProto".to_string()),
5306                            target_path: Some("/svc/fuchsia.sys2.DebugProto".to_string()),
5307                            availability: Some(fdecl::Availability::Required),
5308                            ..Default::default()
5309                        }
5310                    ),
5311                    fdecl::Use::Protocol (
5312                        fdecl::UseProtocol {
5313                            dependency_type: Some(fdecl::DependencyType::Strong),
5314                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5315                                name: "logger".into(),
5316                                collection: None,
5317                            })),
5318                            source_dictionary: Some("in/dict".into()),
5319                            source_name: Some("fuchsia.sys2.DictionaryProto".to_string()),
5320                            target_path: Some("/svc/fuchsia.sys2.DictionaryProto".to_string()),
5321                            availability: Some(fdecl::Availability::Required),
5322                            ..Default::default()
5323                        }
5324                    ),
5325                    fdecl::Use::Protocol (
5326                        fdecl::UseProtocol {
5327                            dependency_type: Some(fdecl::DependencyType::Strong),
5328                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5329                            source_name: Some("fuchsia.sys2.Echo".to_string()),
5330                            target_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
5331                            availability: Some(fdecl::Availability::Transitional),
5332                            ..Default::default()
5333                        }
5334                    ),
5335                    fdecl::Use::Service (
5336                        fdecl::UseService {
5337                            dependency_type: Some(fdecl::DependencyType::Strong),
5338                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5339                            source_dictionary: Some("dict".into()),
5340                            source_name: Some("fuchsia.sys2.EchoService".to_string()),
5341                            target_path: Some("/svc/fuchsia.sys2.EchoService".to_string()),
5342                            availability: Some(fdecl::Availability::Required),
5343                            ..Default::default()
5344                        }
5345                    ),
5346                    fdecl::Use::Directory (
5347                        fdecl::UseDirectory {
5348                            dependency_type: Some(fdecl::DependencyType::Strong),
5349                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5350                            source_name: Some("assets".to_string()),
5351                            target_path: Some("/data/assets".to_string()),
5352                            rights: Some(fio::Operations::READ_BYTES),
5353                            subdir: None,
5354                            availability: Some(fdecl::Availability::Required),
5355                            ..Default::default()
5356                        }
5357                    ),
5358                    fdecl::Use::Directory (
5359                        fdecl::UseDirectory {
5360                            dependency_type: Some(fdecl::DependencyType::Strong),
5361                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5362                            source_name: Some("config".to_string()),
5363                            target_path: Some("/data/config".to_string()),
5364                            rights: Some(fio::Operations::READ_BYTES),
5365                            subdir: Some("fonts".to_string()),
5366                            availability: Some(fdecl::Availability::Required),
5367                            ..Default::default()
5368                        }
5369                    ),
5370                    fdecl::Use::Storage (
5371                        fdecl::UseStorage {
5372                            source_name: Some("hippos".to_string()),
5373                            target_path: Some("/hippos".to_string()),
5374                            availability: Some(fdecl::Availability::Required),
5375                            ..Default::default()
5376                        }
5377                    ),
5378                    fdecl::Use::Storage (
5379                        fdecl::UseStorage {
5380                            source_name: Some("cache".to_string()),
5381                            target_path: Some("/tmp".to_string()),
5382                            availability: Some(fdecl::Availability::Required),
5383                            ..Default::default()
5384                        }
5385                    ),
5386                    fdecl::Use::EventStream(fdecl::UseEventStream {
5387                        source_name: Some("bar_stream".to_string()),
5388                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5389                        target_path: Some("/svc/fuchsia.component.EventStream".to_string()),
5390                        availability: Some(fdecl::Availability::Required),
5391                        ..Default::default()
5392                    }),
5393                    fdecl::Use::EventStream(fdecl::UseEventStream {
5394                        source_name: Some("foobar".to_string()),
5395                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
5396                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5397                        target_path: Some("/event_stream/another".to_string()),
5398                        availability: Some(fdecl::Availability::Required),
5399                        ..Default::default()
5400                    }),
5401                    fdecl::Use::EventStream(fdecl::UseEventStream {
5402                        source_name: Some("stream".to_string()),
5403                        scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
5404                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5405                        target_path: Some("/event_stream/another".to_string()),
5406                        availability: Some(fdecl::Availability::Required),
5407                        ..Default::default()
5408                    }),
5409                    fdecl::Use::Runner(fdecl::UseRunner {
5410                        source_name: Some("usain".to_string()),
5411                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5412                        ..Default::default()
5413                    }),
5414                    fdecl::Use::Dictionary(fdecl::UseDictionary {
5415                        source_name: Some("toolbox".to_string()),
5416                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5417                        target_path: Some("/svc".to_string()),
5418                        dependency_type: Some(fdecl::DependencyType::Strong),
5419                        availability: Some(fdecl::Availability::Required),
5420                        ..Default::default()
5421                    }),
5422                ]),
5423                collections: Some(vec![
5424                    fdecl::Collection{
5425                        name:Some("modular".to_string()),
5426                        durability:Some(fdecl::Durability::Transient),
5427                        ..Default::default()
5428                    },
5429                ]),
5430                capabilities: Some(vec![
5431                    fdecl::Capability::Protocol(fdecl::Protocol {
5432                        name: Some("fuchsia.sys2.Echo".to_string()),
5433                        source_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
5434                        ..Default::default()
5435                    }),
5436                    fdecl::Capability::Config(fdecl::Configuration {
5437                        name: Some("fuchsia.config.Config".to_string()),
5438                        value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
5439                        ..Default::default()
5440                    }),
5441                    fdecl::Capability::Storage(fdecl::Storage {
5442                        name: Some("data-storage".to_string()),
5443                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5444                        backing_dir: Some("minfs".to_string()),
5445                        subdir: None,
5446                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
5447                        ..Default::default()
5448                    }),
5449                ]),
5450                children: Some(vec![
5451                    fdecl::Child{
5452                        name:Some("logger".to_string()),
5453                        url:Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
5454                        startup:Some(fdecl::StartupMode::Lazy),
5455                        environment: Some("env_one".to_string()),
5456                        ..Default::default()
5457                    }
5458                ]),
5459                environments: Some(vec![
5460                    fdecl::Environment {
5461                        name: Some("env_one".to_string()),
5462                        extends: Some(fdecl::EnvironmentExtends::Realm),
5463                        ..Default::default()
5464                    },
5465                ]),
5466                config: None,
5467                ..default_component_decl()
5468            },
5469        },
5470
5471        test_compile_expose => {
5472            input = json!({
5473                "expose": [
5474                    {
5475                        "protocol": "fuchsia.logger.Log",
5476                        "from": "#logger",
5477                        "as": "fuchsia.logger.LegacyLog",
5478                        "to": "parent"
5479                    },
5480                    {
5481                        "protocol": [ "A", "B" ],
5482                        "from": "self",
5483                        "to": "parent"
5484                    },
5485                    {
5486                        "protocol": "C",
5487                        "from": "#data-storage",
5488                    },
5489                    {
5490                        "protocol": "D",
5491                        "from": "#logger/in/dict",
5492                        "as": "E",
5493                    },
5494                    {
5495                        "service": "F",
5496                        "from": "#logger/in/dict",
5497                    },
5498                    {
5499                        "service": "svc",
5500                        "from": [ "#logger", "#coll", "self" ],
5501                    },
5502                    {
5503                        "directory": "blob",
5504                        "from": "self",
5505                        "to": "framework",
5506                        "rights": ["r*"],
5507                    },
5508                    {
5509                        "directory": [ "blob2", "blob3" ],
5510                        "from": "#logger",
5511                        "to": "parent",
5512                    },
5513                    { "directory": "hub", "from": "framework" },
5514                    { "runner": "web", "from": "#logger", "to": "parent", "as": "web-rename" },
5515                    { "runner": [ "runner_a", "runner_b" ], "from": "#logger" },
5516                    { "resolver": "my_resolver", "from": "#logger", "to": "parent", "as": "pkg_resolver" },
5517                    { "resolver": [ "resolver_a", "resolver_b" ], "from": "#logger" },
5518                    { "dictionary": [ "dictionary_a", "dictionary_b" ], "from": "#logger" },
5519                ],
5520                "capabilities": [
5521                    { "protocol": "A" },
5522                    { "protocol": "B" },
5523                    { "service": "svc" },
5524                    {
5525                        "directory": "blob",
5526                        "path": "/volumes/blobfs/blob",
5527                        "rights": ["r*"],
5528                    },
5529                    {
5530                        "runner": "web",
5531                        "path": "/svc/fuchsia.component.ComponentRunner",
5532                    },
5533                    {
5534                        "storage": "data-storage",
5535                        "from": "parent",
5536                        "backing_dir": "minfs",
5537                        "storage_id": "static_instance_id_or_moniker",
5538                    },
5539                ],
5540                "children": [
5541                    {
5542                        "name": "logger",
5543                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
5544                    },
5545                ],
5546                "collections": [
5547                    {
5548                        "name": "coll",
5549                        "durability": "transient",
5550                    },
5551                ],
5552            }),
5553            output = fdecl::Component {
5554                exposes: Some(vec![
5555                    fdecl::Expose::Protocol (
5556                        fdecl::ExposeProtocol {
5557                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5558                                name: "logger".to_string(),
5559                                collection: None,
5560                            })),
5561                            source_name: Some("fuchsia.logger.Log".to_string()),
5562                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5563                            target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5564                            availability: Some(fdecl::Availability::Required),
5565                            ..Default::default()
5566                        }
5567                    ),
5568                    fdecl::Expose::Protocol (
5569                        fdecl::ExposeProtocol {
5570                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5571                            source_name: Some("A".to_string()),
5572                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5573                            target_name: Some("A".to_string()),
5574                            availability: Some(fdecl::Availability::Required),
5575                            ..Default::default()
5576                        }
5577                    ),
5578                    fdecl::Expose::Protocol (
5579                        fdecl::ExposeProtocol {
5580                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5581                            source_name: Some("B".to_string()),
5582                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5583                            target_name: Some("B".to_string()),
5584                            availability: Some(fdecl::Availability::Required),
5585                            ..Default::default()
5586                        }
5587                    ),
5588                    fdecl::Expose::Protocol (
5589                        fdecl::ExposeProtocol {
5590                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5591                                name: "data-storage".to_string(),
5592                            })),
5593                            source_name: Some("C".to_string()),
5594                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5595                            target_name: Some("C".to_string()),
5596                            availability: Some(fdecl::Availability::Required),
5597                            ..Default::default()
5598                        }
5599                    ),
5600                    fdecl::Expose::Protocol (
5601                        fdecl::ExposeProtocol {
5602                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5603                                name: "logger".to_string(),
5604                                collection: None,
5605                            })),
5606                            source_dictionary: Some("in/dict".into()),
5607                            source_name: Some("D".to_string()),
5608                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5609                            target_name: Some("E".to_string()),
5610                            availability: Some(fdecl::Availability::Required),
5611                            ..Default::default()
5612                        }
5613                    ),
5614                    fdecl::Expose::Service (
5615                        fdecl::ExposeService {
5616                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5617                                name: "logger".into(),
5618                                collection: None,
5619                            })),
5620                            source_name: Some("F".into()),
5621                            source_dictionary: Some("in/dict".into()),
5622                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5623                            target_name: Some("F".into()),
5624                            availability: Some(fdecl::Availability::Required),
5625                            ..Default::default()
5626                        }
5627                    ),
5628                    fdecl::Expose::Service (
5629                        fdecl::ExposeService {
5630                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5631                                name: "logger".into(),
5632                                collection: None,
5633                            })),
5634                            source_name: Some("svc".into()),
5635                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5636                            target_name: Some("svc".into()),
5637                            availability: Some(fdecl::Availability::Required),
5638                            ..Default::default()
5639                        }
5640                    ),
5641                    fdecl::Expose::Service (
5642                        fdecl::ExposeService {
5643                            source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5644                                name: "coll".into(),
5645                            })),
5646                            source_name: Some("svc".into()),
5647                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5648                            target_name: Some("svc".into()),
5649                            availability: Some(fdecl::Availability::Required),
5650                            ..Default::default()
5651                        }
5652                    ),
5653                    fdecl::Expose::Service (
5654                        fdecl::ExposeService {
5655                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5656                            source_name: Some("svc".into()),
5657                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5658                            target_name: Some("svc".into()),
5659                            availability: Some(fdecl::Availability::Required),
5660                            ..Default::default()
5661                        }
5662                    ),
5663                    fdecl::Expose::Directory (
5664                        fdecl::ExposeDirectory {
5665                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5666                            source_name: Some("blob".to_string()),
5667                            target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
5668                            target_name: Some("blob".to_string()),
5669                            rights: Some(
5670                                fio::Operations::CONNECT | fio::Operations::ENUMERATE |
5671                                fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
5672                                fio::Operations::GET_ATTRIBUTES
5673                            ),
5674                            subdir: None,
5675                            availability: Some(fdecl::Availability::Required),
5676                            ..Default::default()
5677                        }
5678                    ),
5679                    fdecl::Expose::Directory (
5680                        fdecl::ExposeDirectory {
5681                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5682                                name: "logger".to_string(),
5683                                collection: None,
5684                            })),
5685                            source_name: Some("blob2".to_string()),
5686                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5687                            target_name: Some("blob2".to_string()),
5688                            rights: None,
5689                            subdir: None,
5690                            availability: Some(fdecl::Availability::Required),
5691                            ..Default::default()
5692                        }
5693                    ),
5694                    fdecl::Expose::Directory (
5695                        fdecl::ExposeDirectory {
5696                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5697                                name: "logger".to_string(),
5698                                collection: None,
5699                            })),
5700                            source_name: Some("blob3".to_string()),
5701                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5702                            target_name: Some("blob3".to_string()),
5703                            rights: None,
5704                            subdir: None,
5705                            availability: Some(fdecl::Availability::Required),
5706                            ..Default::default()
5707                        }
5708                    ),
5709                    fdecl::Expose::Directory (
5710                        fdecl::ExposeDirectory {
5711                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
5712                            source_name: Some("hub".to_string()),
5713                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5714                            target_name: Some("hub".to_string()),
5715                            rights: None,
5716                            subdir: None,
5717                            availability: Some(fdecl::Availability::Required),
5718                            ..Default::default()
5719                        }
5720                    ),
5721                    fdecl::Expose::Runner (
5722                        fdecl::ExposeRunner {
5723                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5724                                name: "logger".to_string(),
5725                                collection: None,
5726                            })),
5727                            source_name: Some("web".to_string()),
5728                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5729                            target_name: Some("web-rename".to_string()),
5730                            ..Default::default()
5731                        }
5732                    ),
5733                    fdecl::Expose::Runner (
5734                        fdecl::ExposeRunner {
5735                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5736                                name: "logger".to_string(),
5737                                collection: None,
5738                            })),
5739                            source_name: Some("runner_a".to_string()),
5740                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5741                            target_name: Some("runner_a".to_string()),
5742                            ..Default::default()
5743                        }
5744                    ),
5745                    fdecl::Expose::Runner (
5746                        fdecl::ExposeRunner {
5747                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5748                                name: "logger".to_string(),
5749                                collection: None,
5750                            })),
5751                            source_name: Some("runner_b".to_string()),
5752                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5753                            target_name: Some("runner_b".to_string()),
5754                            ..Default::default()
5755                        }
5756                    ),
5757                    fdecl::Expose::Resolver (
5758                        fdecl::ExposeResolver {
5759                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5760                                name: "logger".to_string(),
5761                                collection: None,
5762                            })),
5763                            source_name: Some("my_resolver".to_string()),
5764                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5765                            target_name: Some("pkg_resolver".to_string()),
5766                            ..Default::default()
5767                        }
5768                    ),
5769                    fdecl::Expose::Resolver (
5770                        fdecl::ExposeResolver {
5771                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5772                                name: "logger".to_string(),
5773                                collection: None,
5774                            })),
5775                            source_name: Some("resolver_a".to_string()),
5776                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5777                            target_name: Some("resolver_a".to_string()),
5778                            ..Default::default()
5779                        }
5780                    ),
5781                    fdecl::Expose::Resolver (
5782                        fdecl::ExposeResolver {
5783                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5784                                name: "logger".to_string(),
5785                                collection: None,
5786                            })),
5787                            source_name: Some("resolver_b".to_string()),
5788                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5789                            target_name: Some("resolver_b".to_string()),
5790                            ..Default::default()
5791                        }
5792                    ),
5793                   fdecl::Expose::Dictionary (
5794                        fdecl::ExposeDictionary {
5795                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5796                                name: "logger".to_string(),
5797                                collection: None,
5798                            })),
5799                            source_name: Some("dictionary_a".to_string()),
5800                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5801                            target_name: Some("dictionary_a".to_string()),
5802                            availability: Some(fdecl::Availability::Required),
5803                            ..Default::default()
5804                        }
5805                    ),
5806                    fdecl::Expose::Dictionary (
5807                        fdecl::ExposeDictionary {
5808                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5809                                name: "logger".to_string(),
5810                                collection: None,
5811                            })),
5812                            source_name: Some("dictionary_b".to_string()),
5813                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5814                            target_name: Some("dictionary_b".to_string()),
5815                            availability: Some(fdecl::Availability::Required),
5816                            ..Default::default()
5817                        }
5818                    ),
5819                ]),
5820                offers: None,
5821                capabilities: Some(vec![
5822                    fdecl::Capability::Protocol (
5823                        fdecl::Protocol {
5824                            name: Some("A".to_string()),
5825                            source_path: Some("/svc/A".to_string()),
5826                            ..Default::default()
5827                        }
5828                    ),
5829                    fdecl::Capability::Protocol (
5830                        fdecl::Protocol {
5831                            name: Some("B".to_string()),
5832                            source_path: Some("/svc/B".to_string()),
5833                            ..Default::default()
5834                        }
5835                    ),
5836                    fdecl::Capability::Service (
5837                        fdecl::Service {
5838                            name: Some("svc".to_string()),
5839                            source_path: Some("/svc/svc".to_string()),
5840                            ..Default::default()
5841                        }
5842                    ),
5843                    fdecl::Capability::Directory (
5844                        fdecl::Directory {
5845                            name: Some("blob".to_string()),
5846                            source_path: Some("/volumes/blobfs/blob".to_string()),
5847                            rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
5848                                fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
5849                                fio::Operations::GET_ATTRIBUTES
5850                            ),
5851                            ..Default::default()
5852                        }
5853                    ),
5854                    fdecl::Capability::Runner (
5855                        fdecl::Runner {
5856                            name: Some("web".to_string()),
5857                            source_path: Some("/svc/fuchsia.component.ComponentRunner".to_string()),
5858                            ..Default::default()
5859                        }
5860                    ),
5861                    fdecl::Capability::Storage(fdecl::Storage {
5862                        name: Some("data-storage".to_string()),
5863                        source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5864                        backing_dir: Some("minfs".to_string()),
5865                        subdir: None,
5866                        storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
5867                        ..Default::default()
5868                    }),
5869                ]),
5870                children: Some(vec![
5871                    fdecl::Child {
5872                        name: Some("logger".to_string()),
5873                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
5874                        startup: Some(fdecl::StartupMode::Lazy),
5875                        ..Default::default()
5876                    }
5877                ]),
5878                collections: Some(vec![
5879                    fdecl::Collection {
5880                        name: Some("coll".to_string()),
5881                        durability: Some(fdecl::Durability::Transient),
5882                        ..Default::default()
5883                    }
5884                ]),
5885                ..default_component_decl()
5886            },
5887        },
5888
5889        test_compile_expose_other_availability => {
5890            input = json!({
5891                "expose": [
5892                    {
5893                        "protocol": "fuchsia.logger.Log",
5894                        "from": "#logger",
5895                        "as": "fuchsia.logger.LegacyLog_default",
5896                        "to": "parent"
5897                    },
5898                    {
5899                        "protocol": "fuchsia.logger.Log",
5900                        "from": "#logger",
5901                        "as": "fuchsia.logger.LegacyLog_required",
5902                        "to": "parent",
5903                        "availability": "required"
5904                    },
5905                    {
5906                        "protocol": "fuchsia.logger.Log",
5907                        "from": "#logger",
5908                        "as": "fuchsia.logger.LegacyLog_optional",
5909                        "to": "parent",
5910                        "availability": "optional"
5911                    },
5912                    {
5913                        "protocol": "fuchsia.logger.Log",
5914                        "from": "#logger",
5915                        "as": "fuchsia.logger.LegacyLog_same_as_target",
5916                        "to": "parent",
5917                        "availability": "same_as_target"
5918                    },
5919                    {
5920                        "protocol": "fuchsia.logger.Log",
5921                        "from": "#logger",
5922                        "as": "fuchsia.logger.LegacyLog_transitional",
5923                        "to": "parent",
5924                        "availability": "transitional"
5925                    },
5926                ],
5927                "children": [
5928                    {
5929                        "name": "logger",
5930                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
5931                    },
5932                ],
5933            }),
5934            output = fdecl::Component {
5935                exposes: Some(vec![
5936                    fdecl::Expose::Protocol (
5937                        fdecl::ExposeProtocol {
5938                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5939                                name: "logger".to_string(),
5940                                collection: None,
5941                            })),
5942                            source_name: Some("fuchsia.logger.Log".to_string()),
5943                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5944                            target_name: Some("fuchsia.logger.LegacyLog_default".to_string()),
5945                            availability: Some(fdecl::Availability::Required),
5946                            ..Default::default()
5947                        }
5948                    ),
5949                    fdecl::Expose::Protocol (
5950                        fdecl::ExposeProtocol {
5951                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5952                                name: "logger".to_string(),
5953                                collection: None,
5954                            })),
5955                            source_name: Some("fuchsia.logger.Log".to_string()),
5956                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5957                            target_name: Some("fuchsia.logger.LegacyLog_required".to_string()),
5958                            availability: Some(fdecl::Availability::Required),
5959                            ..Default::default()
5960                        }
5961                    ),
5962                    fdecl::Expose::Protocol (
5963                        fdecl::ExposeProtocol {
5964                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5965                                name: "logger".to_string(),
5966                                collection: None,
5967                            })),
5968                            source_name: Some("fuchsia.logger.Log".to_string()),
5969                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5970                            target_name: Some("fuchsia.logger.LegacyLog_optional".to_string()),
5971                            availability: Some(fdecl::Availability::Optional),
5972                            ..Default::default()
5973                        }
5974                    ),
5975                    fdecl::Expose::Protocol (
5976                        fdecl::ExposeProtocol {
5977                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5978                                name: "logger".to_string(),
5979                                collection: None,
5980                            })),
5981                            source_name: Some("fuchsia.logger.Log".to_string()),
5982                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5983                            target_name: Some("fuchsia.logger.LegacyLog_same_as_target".to_string()),
5984                            availability: Some(fdecl::Availability::SameAsTarget),
5985                            ..Default::default()
5986                        }
5987                    ),
5988                    fdecl::Expose::Protocol (
5989                        fdecl::ExposeProtocol {
5990                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5991                                name: "logger".to_string(),
5992                                collection: None,
5993                            })),
5994                            source_name: Some("fuchsia.logger.Log".to_string()),
5995                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5996                            target_name: Some("fuchsia.logger.LegacyLog_transitional".to_string()),
5997                            availability: Some(fdecl::Availability::Transitional),
5998                            ..Default::default()
5999                        }
6000                    ),
6001                ]),
6002                offers: None,
6003                capabilities: None,
6004                children: Some(vec![
6005                    fdecl::Child {
6006                        name: Some("logger".to_string()),
6007                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6008                        startup: Some(fdecl::StartupMode::Lazy),
6009                        environment: None,
6010                        on_terminate: None,
6011                        ..Default::default()
6012                    }
6013                ]),
6014                ..default_component_decl()
6015            },
6016        },
6017
6018        test_compile_expose_source_availability_unknown => {
6019            input = json!({
6020                "expose": [
6021                    {
6022                        "protocol": "fuchsia.logger.Log",
6023                        "from": "#non-existent",
6024                        "as": "fuchsia.logger.LegacyLog_non_existent",
6025                        "availability": "optional",
6026                        "source_availability": "unknown"
6027                    },
6028                    {
6029                        "protocol": "fuchsia.logger.Log",
6030                        "from": "#non-existent/dict",
6031                        "as": "fuchsia.logger.LegacyLog_non_existent2",
6032                        "availability": "optional",
6033                        "source_availability": "unknown"
6034                    },
6035                    {
6036                        "protocol": "fuchsia.logger.Log",
6037                        "from": "#logger",
6038                        "as": "fuchsia.logger.LegacyLog_child_exist",
6039                        "availability": "optional",
6040                        "source_availability": "unknown"
6041                    },
6042                ],
6043                "children": [
6044                    {
6045                        "name": "logger",
6046                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6047                    },
6048                ],
6049            }),
6050            output = fdecl::Component {
6051                exposes: Some(vec![
6052                    fdecl::Expose::Protocol (
6053                        fdecl::ExposeProtocol {
6054                            source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6055                            source_name: Some("fuchsia.logger.Log".to_string()),
6056                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6057                            target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
6058                            availability: Some(fdecl::Availability::Optional),
6059                            ..Default::default()
6060                        }
6061                    ),
6062                    fdecl::Expose::Protocol (
6063                        fdecl::ExposeProtocol {
6064                            source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6065                            source_name: Some("fuchsia.logger.Log".to_string()),
6066                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6067                            target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
6068                            availability: Some(fdecl::Availability::Optional),
6069                            ..Default::default()
6070                        }
6071                    ),
6072                    fdecl::Expose::Protocol (
6073                        fdecl::ExposeProtocol {
6074                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6075                                name: "logger".to_string(),
6076                                collection: None,
6077                            })),
6078                            source_name: Some("fuchsia.logger.Log".to_string()),
6079                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6080                            target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
6081                            availability: Some(fdecl::Availability::Optional),
6082                            ..Default::default()
6083                        }
6084                    ),
6085                ]),
6086                children: Some(vec![
6087                    fdecl::Child {
6088                        name: Some("logger".to_string()),
6089                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6090                        startup: Some(fdecl::StartupMode::Lazy),
6091                        ..Default::default()
6092                    }
6093                ]),
6094                ..default_component_decl()
6095            },
6096        },
6097
6098        test_compile_offer_target_availability_unknown => {
6099            input = json!({
6100                "offer": [
6101                    {
6102                        "protocol": "fuchsia.logger.Log",
6103                        "from": "#logger",
6104                        "to": "#non-existent",
6105                        "target_availability": "unknown",
6106                    },
6107                    {
6108                        "protocol": "fuchsia.logger.Log",
6109                        "from": "#logger",
6110                        "to": "self/non-existent-dict",
6111                        "target_availability": "unknown",
6112                    },
6113                ],
6114                "children": [
6115                    {
6116                        "name": "logger",
6117                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6118                    },
6119                ],
6120            }),
6121            output = fdecl::Component {
6122                offers: Some(vec![]),
6123                children: Some(vec![
6124                    fdecl::Child {
6125                        name: Some("logger".to_string()),
6126                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6127                        startup: Some(fdecl::StartupMode::Lazy),
6128                        ..Default::default()
6129                    },
6130                ]),
6131                ..default_component_decl()
6132            },
6133        },
6134
6135        test_compile_offer_source_availability_unknown => {
6136            input = json!({
6137                "offer": [
6138                    {
6139                        "protocol": "fuchsia.logger.Log",
6140                        "from": "#non-existent",
6141                        "as": "fuchsia.logger.LegacyLog_non_existent",
6142                        "to": "#target",
6143                        "availability": "optional",
6144                        "source_availability": "unknown"
6145                    },
6146                    {
6147                        "protocol": "fuchsia.logger.Log",
6148                        "from": "#non-existent/dict",
6149                        "as": "fuchsia.logger.LegacyLog_non_existent2",
6150                        "to": "#target",
6151                        "availability": "optional",
6152                        "source_availability": "unknown"
6153                    },
6154                    {
6155                        "protocol": "fuchsia.logger.Log",
6156                        "from": "#logger",
6157                        "as": "fuchsia.logger.LegacyLog_child_exist",
6158                        "to": "#target",
6159                        "availability": "optional",
6160                        "source_availability": "unknown"
6161                    },
6162                ],
6163                "children": [
6164                    {
6165                        "name": "logger",
6166                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6167                    },
6168                    {
6169                        "name": "target",
6170                        "url": "#meta/target.cm"
6171                    },
6172                ],
6173            }),
6174            output = fdecl::Component {
6175                offers: Some(vec![
6176                    fdecl::Offer::Protocol (
6177                        fdecl::OfferProtocol {
6178                            source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6179                            source_name: Some("fuchsia.logger.Log".to_string()),
6180                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6181                                name: "target".to_string(),
6182                                collection: None,
6183                            })),
6184                            target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
6185                            dependency_type: Some(fdecl::DependencyType::Strong),
6186                            availability: Some(fdecl::Availability::Optional),
6187                            ..Default::default()
6188                        }
6189                    ),
6190                    fdecl::Offer::Protocol (
6191                        fdecl::OfferProtocol {
6192                            source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6193                            source_name: Some("fuchsia.logger.Log".to_string()),
6194                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6195                                name: "target".to_string(),
6196                                collection: None,
6197                            })),
6198                            target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
6199                            dependency_type: Some(fdecl::DependencyType::Strong),
6200                            availability: Some(fdecl::Availability::Optional),
6201                            ..Default::default()
6202                        }
6203                    ),
6204                    fdecl::Offer::Protocol (
6205                        fdecl::OfferProtocol {
6206                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6207                                name: "logger".to_string(),
6208                                collection: None,
6209                            })),
6210                            source_name: Some("fuchsia.logger.Log".to_string()),
6211                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6212                                name: "target".to_string(),
6213                                collection: None,
6214                            })),
6215                            target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
6216                            dependency_type: Some(fdecl::DependencyType::Strong),
6217                            availability: Some(fdecl::Availability::Optional),
6218                            ..Default::default()
6219                        }
6220                    ),
6221                ]),
6222                children: Some(vec![
6223                    fdecl::Child {
6224                        name: Some("logger".to_string()),
6225                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6226                        startup: Some(fdecl::StartupMode::Lazy),
6227                        ..Default::default()
6228                    },
6229                    fdecl::Child {
6230                        name: Some("target".to_string()),
6231                        url: Some("#meta/target.cm".to_string()),
6232                        startup: Some(fdecl::StartupMode::Lazy),
6233                        ..Default::default()
6234                    },
6235                ]),
6236                ..default_component_decl()
6237            },
6238        },
6239
6240        test_compile_offer => {
6241            input = json!({
6242                "offer": [
6243                    {
6244                        "protocol": "fuchsia.logger.LegacyLog",
6245                        "from": "#logger",
6246                        "to": "#netstack", // Verifies compilation of singleton "to:".
6247                        "dependency": "weak"
6248                    },
6249                    {
6250                        "protocol": "fuchsia.logger.LegacyLog",
6251                        "from": "#logger",
6252                        "to": [ "#modular" ], // Verifies compilation of "to:" as array of one element.
6253                        "as": "fuchsia.logger.LegacySysLog",
6254                        "dependency": "strong"
6255                    },
6256                    {
6257                        "protocol": [
6258                            "fuchsia.setui.SetUiService",
6259                            "fuchsia.test.service.Name"
6260                        ],
6261                        "from": "parent",
6262                        "to": [ "#modular" ],
6263                        "availability": "optional"
6264                    },
6265                    {
6266                        "protocol": "fuchsia.sys2.StorageAdmin",
6267                        "from": "#data",
6268                        "to": [ "#modular" ],
6269                    },
6270                    {
6271                        "protocol": "fuchsia.sys2.FromDict",
6272                        "from": "parent/in/dict",
6273                        "to": [ "#modular" ],
6274                    },
6275                    {
6276                        "service": "svc",
6277                        "from": [ "parent", "self", "#logger", "#modular" ],
6278                        "to": "#netstack",
6279                    },
6280                    {
6281                        "service": "fuchsia.sys2.FromDictService",
6282                        "from": [ "parent/in/dict"],
6283                        "to": "#modular",
6284                        "dependency": "weak",
6285                    },
6286                    {
6287                        "directory": "assets",
6288                        "from": "parent",
6289                        "to": [ "#netstack" ],
6290                        "dependency": "weak",
6291                        "availability": "same_as_target"
6292                    },
6293                    {
6294                        "directory": [ "assets2", "assets3" ],
6295                        "from": "parent",
6296                        "to": [ "#modular", "#netstack" ],
6297                    },
6298                    {
6299                        "directory": "data",
6300                        "from": "parent",
6301                        "to": [ "#modular" ],
6302                        "as": "assets",
6303                        "subdir": "index/file",
6304                        "dependency": "strong"
6305                    },
6306                    {
6307                        "directory": "hub",
6308                        "from": "framework",
6309                        "to": [ "#modular" ],
6310                        "as": "hub",
6311                    },
6312                    {
6313                        "storage": "data",
6314                        "from": "self",
6315                        "to": [
6316                            "#netstack",
6317                            "#modular"
6318                        ],
6319                    },
6320                    {
6321                        "storage": [ "storage_a", "storage_b" ],
6322                        "from": "parent",
6323                        "to": "#netstack",
6324                    },
6325                    {
6326                        "runner": "elf",
6327                        "from": "parent",
6328                        "to": [ "#modular" ],
6329                        "as": "elf-renamed",
6330                    },
6331                    {
6332                        "runner": [ "runner_a", "runner_b" ],
6333                        "from": "parent",
6334                        "to": "#netstack",
6335                    },
6336                    {
6337                        "resolver": "my_resolver",
6338                        "from": "parent",
6339                        "to": [ "#modular" ],
6340                        "as": "pkg_resolver",
6341                    },
6342                    {
6343                        "resolver": [ "resolver_a", "resolver_b" ],
6344                        "from": "parent",
6345                        "to": "#netstack",
6346                    },
6347                    {
6348                        "dictionary": [ "dictionary_a", "dictionary_b" ],
6349                        "from": "parent",
6350                        "to": "#netstack",
6351                    },
6352                    {
6353                        "event_stream": [
6354                            "running",
6355                            "started",
6356                        ],
6357                        "from": "parent",
6358                        "to": "#netstack",
6359                    },
6360                    {
6361                        "event_stream": "stopped",
6362                        "from": "parent",
6363                        "to": "#netstack",
6364                        "as": "some_other_event",
6365                    },
6366                ],
6367                "children": [
6368                    {
6369                        "name": "logger",
6370                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6371                    },
6372                    {
6373                        "name": "netstack",
6374                        "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm"
6375                    },
6376                ],
6377                "collections": [
6378                    {
6379                        "name": "modular",
6380                        "durability": "transient",
6381                    },
6382                ],
6383                "capabilities": [
6384                    {
6385                        "service": "svc",
6386                    },
6387                    {
6388                        "storage": "data",
6389                        "backing_dir": "minfs",
6390                        "from": "#logger",
6391                        "storage_id": "static_instance_id_or_moniker",
6392                    },
6393                ],
6394            }),
6395            output = fdecl::Component {
6396                offers: Some(vec![
6397                    fdecl::Offer::Protocol (
6398                        fdecl::OfferProtocol {
6399                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6400                                name: "logger".to_string(),
6401                                collection: None,
6402                            })),
6403                            source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6404                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6405                                name: "netstack".to_string(),
6406                                collection: None,
6407                            })),
6408                            target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6409                            dependency_type: Some(fdecl::DependencyType::Weak),
6410                            availability: Some(fdecl::Availability::Required),
6411                            ..Default::default()
6412                        }
6413                    ),
6414                    fdecl::Offer::Protocol (
6415                        fdecl::OfferProtocol {
6416                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6417                                name: "logger".to_string(),
6418                                collection: None,
6419                            })),
6420                            source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6421                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6422                                name: "modular".to_string(),
6423                            })),
6424                            target_name: Some("fuchsia.logger.LegacySysLog".to_string()),
6425                            dependency_type: Some(fdecl::DependencyType::Strong),
6426                            availability: Some(fdecl::Availability::Required),
6427                            ..Default::default()
6428                        }
6429                    ),
6430                    fdecl::Offer::Protocol (
6431                        fdecl::OfferProtocol {
6432                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6433                            source_name: Some("fuchsia.setui.SetUiService".to_string()),
6434                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6435                                name: "modular".to_string(),
6436                            })),
6437                            target_name: Some("fuchsia.setui.SetUiService".to_string()),
6438                            dependency_type: Some(fdecl::DependencyType::Strong),
6439                            availability: Some(fdecl::Availability::Optional),
6440                            ..Default::default()
6441                        }
6442                    ),
6443                    fdecl::Offer::Protocol (
6444                        fdecl::OfferProtocol {
6445                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6446                            source_name: Some("fuchsia.test.service.Name".to_string()),
6447                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6448                                name: "modular".to_string(),
6449                            })),
6450                            target_name: Some("fuchsia.test.service.Name".to_string()),
6451                            dependency_type: Some(fdecl::DependencyType::Strong),
6452                            availability: Some(fdecl::Availability::Optional),
6453                            ..Default::default()
6454                        }
6455                    ),
6456                    fdecl::Offer::Protocol (
6457                        fdecl::OfferProtocol {
6458                            source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6459                                name: "data".to_string(),
6460                            })),
6461                            source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6462                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6463                                name: "modular".to_string(),
6464                            })),
6465                            target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6466                            dependency_type: Some(fdecl::DependencyType::Strong),
6467                            availability: Some(fdecl::Availability::Required),
6468                            ..Default::default()
6469                        }
6470                    ),
6471                    fdecl::Offer::Protocol (
6472                        fdecl::OfferProtocol {
6473                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6474                            source_dictionary: Some("in/dict".into()),
6475                            source_name: Some("fuchsia.sys2.FromDict".to_string()),
6476                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6477                                name: "modular".to_string(),
6478                            })),
6479                            target_name: Some("fuchsia.sys2.FromDict".to_string()),
6480                            dependency_type: Some(fdecl::DependencyType::Strong),
6481                            availability: Some(fdecl::Availability::Required),
6482                            ..Default::default()
6483                        }
6484                    ),
6485                    fdecl::Offer::Service (
6486                        fdecl::OfferService {
6487                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6488                            source_name: Some("svc".into()),
6489                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6490                                name: "netstack".into(),
6491                                collection: None,
6492                            })),
6493                            target_name: Some("svc".into()),
6494                            availability: Some(fdecl::Availability::Required),
6495                            dependency_type: Some(fdecl::DependencyType::Strong),
6496                            ..Default::default()
6497                        }
6498                    ),
6499                    fdecl::Offer::Service (
6500                        fdecl::OfferService {
6501                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
6502                            source_name: Some("svc".into()),
6503                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6504                                name: "netstack".into(),
6505                                collection: None,
6506                            })),
6507                            target_name: Some("svc".into()),
6508                            availability: Some(fdecl::Availability::Required),
6509                            dependency_type: Some(fdecl::DependencyType::Strong),
6510                            ..Default::default()
6511                        }
6512                    ),
6513                    fdecl::Offer::Service (
6514                        fdecl::OfferService {
6515                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6516                                name: "logger".into(),
6517                                collection: None,
6518                            })),
6519                            source_name: Some("svc".into()),
6520                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6521                                name: "netstack".into(),
6522                                collection: None,
6523                            })),
6524                            target_name: Some("svc".into()),
6525                            availability: Some(fdecl::Availability::Required),
6526                            dependency_type: Some(fdecl::DependencyType::Strong),
6527                            ..Default::default()
6528                        }
6529                    ),
6530                    fdecl::Offer::Service (
6531                        fdecl::OfferService {
6532                            source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6533                                name: "modular".into(),
6534                            })),
6535                            source_name: Some("svc".into()),
6536                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6537                                name: "netstack".into(),
6538                                collection: None,
6539                            })),
6540                            target_name: Some("svc".into()),
6541                            availability: Some(fdecl::Availability::Required),
6542                            dependency_type: Some(fdecl::DependencyType::Strong),
6543                            ..Default::default()
6544                        }
6545                    ),
6546                    fdecl::Offer::Service (
6547                        fdecl::OfferService {
6548                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6549                            source_name: Some("fuchsia.sys2.FromDictService".into()),
6550                            source_dictionary: Some("in/dict".into()),
6551                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6552                                name: "modular".into(),
6553                            })),
6554                            target_name: Some("fuchsia.sys2.FromDictService".to_string()),
6555                            availability: Some(fdecl::Availability::Required),
6556                            dependency_type: Some(fdecl::DependencyType::Weak),
6557                            ..Default::default()
6558                        }
6559                    ),
6560                    fdecl::Offer::Directory (
6561                        fdecl::OfferDirectory {
6562                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6563                            source_name: Some("assets".to_string()),
6564                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6565                                name: "netstack".to_string(),
6566                                collection: None,
6567                            })),
6568                            target_name: Some("assets".to_string()),
6569                            rights: None,
6570                            subdir: None,
6571                            dependency_type: Some(fdecl::DependencyType::Weak),
6572                            availability: Some(fdecl::Availability::SameAsTarget),
6573                            ..Default::default()
6574                        }
6575                    ),
6576                    fdecl::Offer::Directory (
6577                        fdecl::OfferDirectory {
6578                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6579                            source_name: Some("assets2".to_string()),
6580                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6581                                name: "modular".to_string(),
6582                            })),
6583                            target_name: Some("assets2".to_string()),
6584                            rights: None,
6585                            subdir: None,
6586                            dependency_type: Some(fdecl::DependencyType::Strong),
6587                            availability: Some(fdecl::Availability::Required),
6588                            ..Default::default()
6589                        }
6590                    ),
6591                    fdecl::Offer::Directory (
6592                        fdecl::OfferDirectory {
6593                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6594                            source_name: Some("assets3".to_string()),
6595                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6596                                name: "modular".to_string(),
6597                            })),
6598                            target_name: Some("assets3".to_string()),
6599                            rights: None,
6600                            subdir: None,
6601                            dependency_type: Some(fdecl::DependencyType::Strong),
6602                            availability: Some(fdecl::Availability::Required),
6603                            ..Default::default()
6604                        }
6605                    ),
6606                    fdecl::Offer::Directory (
6607                        fdecl::OfferDirectory {
6608                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6609                            source_name: Some("assets2".to_string()),
6610                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6611                                name: "netstack".to_string(),
6612                                collection: None,
6613                            })),
6614                            target_name: Some("assets2".to_string()),
6615                            rights: None,
6616                            subdir: None,
6617                            dependency_type: Some(fdecl::DependencyType::Strong),
6618                            availability: Some(fdecl::Availability::Required),
6619                            ..Default::default()
6620                        }
6621                    ),
6622                    fdecl::Offer::Directory (
6623                        fdecl::OfferDirectory {
6624                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6625                            source_name: Some("assets3".to_string()),
6626                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6627                                name: "netstack".to_string(),
6628                                collection: None,
6629                            })),
6630                            target_name: Some("assets3".to_string()),
6631                            rights: None,
6632                            subdir: None,
6633                            dependency_type: Some(fdecl::DependencyType::Strong),
6634                            availability: Some(fdecl::Availability::Required),
6635                            ..Default::default()
6636                        }
6637                    ),
6638                    fdecl::Offer::Directory (
6639                        fdecl::OfferDirectory {
6640                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6641                            source_name: Some("data".to_string()),
6642                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6643                                name: "modular".to_string(),
6644                            })),
6645                            target_name: Some("assets".to_string()),
6646                            rights: None,
6647                            subdir: Some("index/file".to_string()),
6648                            dependency_type: Some(fdecl::DependencyType::Strong),
6649                            availability: Some(fdecl::Availability::Required),
6650                            ..Default::default()
6651                        }
6652                    ),
6653                    fdecl::Offer::Directory (
6654                        fdecl::OfferDirectory {
6655                            source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
6656                            source_name: Some("hub".to_string()),
6657                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6658                                name: "modular".to_string(),
6659                            })),
6660                            target_name: Some("hub".to_string()),
6661                            rights: None,
6662                            subdir: None,
6663                            dependency_type: Some(fdecl::DependencyType::Strong),
6664                            availability: Some(fdecl::Availability::Required),
6665                            ..Default::default()
6666                        }
6667                    ),
6668                    fdecl::Offer::Storage (
6669                        fdecl::OfferStorage {
6670                            source_name: Some("data".to_string()),
6671                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
6672                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6673                                name: "netstack".to_string(),
6674                                collection: None,
6675                            })),
6676                            target_name: Some("data".to_string()),
6677                            availability: Some(fdecl::Availability::Required),
6678                            ..Default::default()
6679                        }
6680                    ),
6681                    fdecl::Offer::Storage (
6682                        fdecl::OfferStorage {
6683                            source_name: Some("data".to_string()),
6684                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
6685                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6686                                name: "modular".to_string(),
6687                            })),
6688                            target_name: Some("data".to_string()),
6689                            availability: Some(fdecl::Availability::Required),
6690                            ..Default::default()
6691                        }
6692                    ),
6693                    fdecl::Offer::Storage (
6694                        fdecl::OfferStorage {
6695                            source_name: Some("storage_a".to_string()),
6696                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6697                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6698                                name: "netstack".to_string(),
6699                                collection: None,
6700                            })),
6701                            target_name: Some("storage_a".to_string()),
6702                            availability: Some(fdecl::Availability::Required),
6703                            ..Default::default()
6704                        }
6705                    ),
6706                    fdecl::Offer::Storage (
6707                        fdecl::OfferStorage {
6708                            source_name: Some("storage_b".to_string()),
6709                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6710                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6711                                name: "netstack".to_string(),
6712                                collection: None,
6713                            })),
6714                            target_name: Some("storage_b".to_string()),
6715                            availability: Some(fdecl::Availability::Required),
6716                            ..Default::default()
6717                        }
6718                    ),
6719                    fdecl::Offer::Runner (
6720                        fdecl::OfferRunner {
6721                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6722                            source_name: Some("elf".to_string()),
6723                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6724                                name: "modular".to_string(),
6725                            })),
6726                            target_name: Some("elf-renamed".to_string()),
6727                            ..Default::default()
6728                        }
6729                    ),
6730                    fdecl::Offer::Runner (
6731                        fdecl::OfferRunner {
6732                            source_name: Some("runner_a".to_string()),
6733                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6734                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6735                                name: "netstack".to_string(),
6736                                collection: None,
6737                            })),
6738                            target_name: Some("runner_a".to_string()),
6739                            ..Default::default()
6740                        }
6741                    ),
6742                    fdecl::Offer::Runner (
6743                        fdecl::OfferRunner {
6744                            source_name: Some("runner_b".to_string()),
6745                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6746                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6747                                name: "netstack".to_string(),
6748                                collection: None,
6749                            })),
6750                            target_name: Some("runner_b".to_string()),
6751                            ..Default::default()
6752                        }
6753                    ),
6754                    fdecl::Offer::Resolver (
6755                        fdecl::OfferResolver {
6756                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6757                            source_name: Some("my_resolver".to_string()),
6758                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6759                                name: "modular".to_string(),
6760                            })),
6761                            target_name: Some("pkg_resolver".to_string()),
6762                            ..Default::default()
6763                        }
6764                    ),
6765                    fdecl::Offer::Resolver (
6766                        fdecl::OfferResolver {
6767                            source_name: Some("resolver_a".to_string()),
6768                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6769                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6770                                name: "netstack".to_string(),
6771                                collection: None,
6772                            })),
6773                            target_name: Some("resolver_a".to_string()),
6774                            ..Default::default()
6775                        }
6776                    ),
6777                    fdecl::Offer::Resolver (
6778                        fdecl::OfferResolver {
6779                            source_name: Some("resolver_b".to_string()),
6780                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6781                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6782                                name: "netstack".to_string(),
6783                                collection: None,
6784                            })),
6785                            target_name: Some("resolver_b".to_string()),
6786                            ..Default::default()
6787                        }
6788                    ),
6789                    fdecl::Offer::Dictionary (
6790                        fdecl::OfferDictionary {
6791                            source_name: Some("dictionary_a".to_string()),
6792                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6793                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6794                                name: "netstack".to_string(),
6795                                collection: None,
6796                            })),
6797                            target_name: Some("dictionary_a".to_string()),
6798                            dependency_type: Some(fdecl::DependencyType::Strong),
6799                            availability: Some(fdecl::Availability::Required),
6800                            ..Default::default()
6801                        }
6802                    ),
6803                    fdecl::Offer::Dictionary (
6804                        fdecl::OfferDictionary {
6805                            source_name: Some("dictionary_b".to_string()),
6806                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6807                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6808                                name: "netstack".to_string(),
6809                                collection: None,
6810                            })),
6811                            target_name: Some("dictionary_b".to_string()),
6812                            dependency_type: Some(fdecl::DependencyType::Strong),
6813                            availability: Some(fdecl::Availability::Required),
6814                            ..Default::default()
6815                        }
6816                    ),
6817                    fdecl::Offer::EventStream (
6818                        fdecl::OfferEventStream {
6819                            source_name: Some("running".to_string()),
6820                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6821                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6822                                name: "netstack".to_string(),
6823                                collection: None,
6824                            })),
6825                            target_name: Some("running".to_string()),
6826                            availability: Some(fdecl::Availability::Required),
6827                            ..Default::default()
6828                        }
6829                    ),
6830                    fdecl::Offer::EventStream (
6831                        fdecl::OfferEventStream {
6832                            source_name: Some("started".to_string()),
6833                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6834                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835                                name: "netstack".to_string(),
6836                                collection: None,
6837                            })),
6838                            target_name: Some("started".to_string()),
6839                            availability: Some(fdecl::Availability::Required),
6840                            ..Default::default()
6841                        }
6842                    ),
6843                    fdecl::Offer::EventStream (
6844                        fdecl::OfferEventStream {
6845                            source_name: Some("stopped".to_string()),
6846                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6847                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6848                                name: "netstack".to_string(),
6849                                collection: None,
6850                            })),
6851                            target_name: Some("some_other_event".to_string()),
6852                            availability: Some(fdecl::Availability::Required),
6853                            ..Default::default()
6854                        }
6855                    ),
6856                ]),
6857                capabilities: Some(vec![
6858                    fdecl::Capability::Service (
6859                        fdecl::Service {
6860                            name: Some("svc".into()),
6861                            source_path: Some("/svc/svc".into()),
6862                            ..Default::default()
6863                        },
6864                    ),
6865                    fdecl::Capability::Storage (
6866                        fdecl::Storage {
6867                            name: Some("data".to_string()),
6868                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6869                                name: "logger".to_string(),
6870                                collection: None,
6871                            })),
6872                            backing_dir: Some("minfs".to_string()),
6873                            subdir: None,
6874                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6875                            ..Default::default()
6876                        }
6877                    )
6878                ]),
6879                children: Some(vec![
6880                    fdecl::Child {
6881                        name: Some("logger".to_string()),
6882                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6883                        startup: Some(fdecl::StartupMode::Lazy),
6884                        environment: None,
6885                        on_terminate: None,
6886                        ..Default::default()
6887                    },
6888                    fdecl::Child {
6889                        name: Some("netstack".to_string()),
6890                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6891                        startup: Some(fdecl::StartupMode::Lazy),
6892                        environment: None,
6893                        on_terminate: None,
6894                        ..Default::default()
6895                    },
6896                ]),
6897                collections: Some(vec![
6898                    fdecl::Collection {
6899                        name: Some("modular".to_string()),
6900                        durability: Some(fdecl::Durability::Transient),
6901                        environment: None,
6902                        allowed_offers: None,
6903                        ..Default::default()
6904                    }
6905                ]),
6906                ..default_component_decl()
6907            },
6908        },
6909
6910        test_compile_offer_route_to_dictionary => {
6911            input = json!({
6912                "offer": [
6913                    {
6914                        "protocol": "A",
6915                        "from": "parent/dict/1",
6916                        "to": "self/dict",
6917                    },
6918                    {
6919                        "runner": "B",
6920                        "from": "#child",
6921                        "to": "self/dict",
6922                    },
6923                    {
6924                        "config": "B",
6925                        "from": "parent/dict/2",
6926                        "to": "self/dict",
6927                        "as": "C",
6928                    },
6929                ],
6930                "children": [
6931                    {
6932                        "name": "child",
6933                        "url": "fuchsia-pkg://child"
6934                    },
6935                ],
6936                "capabilities": [
6937                    {
6938                        "dictionary": "dict",
6939                    },
6940                ],
6941            }),
6942            output = fdecl::Component {
6943                offers: Some(vec![
6944                    fdecl::Offer::Protocol (
6945                        fdecl::OfferProtocol {
6946                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6947                            source_dictionary: Some("dict/1".into()),
6948                            source_name: Some("A".into()),
6949                            target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6950                                name: "dict".to_string(),
6951                            })),
6952                            target_name: Some("A".into()),
6953                            dependency_type: Some(fdecl::DependencyType::Strong),
6954                            availability: Some(fdecl::Availability::Required),
6955                            ..Default::default()
6956                        }
6957                    ),
6958                    fdecl::Offer::Runner (
6959                        fdecl::OfferRunner {
6960                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6961                                name: "child".into(),
6962                                collection: None,
6963                            })),
6964                            source_name: Some("B".into()),
6965                            target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6966                                name: "dict".to_string(),
6967                            })),
6968                            target_name: Some("B".into()),
6969                            ..Default::default()
6970                        }
6971                    ),
6972                    fdecl::Offer::Config (
6973                        fdecl::OfferConfiguration {
6974                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6975                            source_dictionary: Some("dict/2".into()),
6976                            source_name: Some("B".into()),
6977                            target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6978                                name: "dict".to_string(),
6979                            })),
6980                            target_name: Some("C".into()),
6981                            availability: Some(fdecl::Availability::Required),
6982                            ..Default::default()
6983                        }
6984                    ),
6985                ]),
6986                capabilities: Some(vec![
6987                    fdecl::Capability::Dictionary (
6988                        fdecl::Dictionary {
6989                            name: Some("dict".into()),
6990                            ..Default::default()
6991                        }
6992                    )
6993                ]),
6994                children: Some(vec![
6995                    fdecl::Child {
6996                        name: Some("child".to_string()),
6997                        url: Some("fuchsia-pkg://child".to_string()),
6998                        startup: Some(fdecl::StartupMode::Lazy),
6999                        ..Default::default()
7000                    },
7001                ]),
7002                ..default_component_decl()
7003            },
7004        },
7005
7006
7007        test_compile_children => {
7008            input = json!({
7009                "children": [
7010                    {
7011                        "name": "logger",
7012                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
7013                    },
7014                    {
7015                        "name": "gmail",
7016                        "url": "https://www.google.com/gmail",
7017                        "startup": "eager",
7018                    },
7019                    {
7020                        "name": "echo",
7021                        "url": "fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm",
7022                        "startup": "lazy",
7023                        "on_terminate": "reboot",
7024                        "environment": "#myenv",
7025                    },
7026                ],
7027                "environments": [
7028                    {
7029                        "name": "myenv",
7030                        "extends": "realm",
7031                    },
7032                ],
7033            }),
7034            output = fdecl::Component {
7035                children: Some(vec![
7036                    fdecl::Child {
7037                        name: Some("logger".to_string()),
7038                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7039                        startup: Some(fdecl::StartupMode::Lazy),
7040                        environment: None,
7041                        on_terminate: None,
7042                        ..Default::default()
7043                    },
7044                    fdecl::Child {
7045                        name: Some("gmail".to_string()),
7046                        url: Some("https://www.google.com/gmail".to_string()),
7047                        startup: Some(fdecl::StartupMode::Eager),
7048                        environment: None,
7049                        on_terminate: None,
7050                        ..Default::default()
7051                    },
7052                    fdecl::Child {
7053                        name: Some("echo".to_string()),
7054                        url: Some("fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm".to_string()),
7055                        startup: Some(fdecl::StartupMode::Lazy),
7056                        environment: Some("myenv".to_string()),
7057                        on_terminate: Some(fdecl::OnTerminate::Reboot),
7058                        ..Default::default()
7059                    }
7060                ]),
7061                environments: Some(vec![
7062                    fdecl::Environment {
7063                        name: Some("myenv".to_string()),
7064                        extends: Some(fdecl::EnvironmentExtends::Realm),
7065                        runners: None,
7066                        resolvers: None,
7067                        stop_timeout_ms: None,
7068                        ..Default::default()
7069                    }
7070                ]),
7071                ..default_component_decl()
7072            },
7073        },
7074
7075        test_compile_collections => {
7076            input = json!({
7077                "collections": [
7078                    {
7079                        "name": "modular",
7080                        "durability": "single_run",
7081                    },
7082                    {
7083                        "name": "tests",
7084                        "durability": "transient",
7085                        "environment": "#myenv",
7086                    },
7087                ],
7088                "environments": [
7089                    {
7090                        "name": "myenv",
7091                        "extends": "realm",
7092                    }
7093                ],
7094            }),
7095            output = fdecl::Component {
7096                collections: Some(vec![
7097                    fdecl::Collection {
7098                        name: Some("modular".to_string()),
7099                        durability: Some(fdecl::Durability::SingleRun),
7100                        environment: None,
7101                        allowed_offers: None,
7102                        ..Default::default()
7103                    },
7104                    fdecl::Collection {
7105                        name: Some("tests".to_string()),
7106                        durability: Some(fdecl::Durability::Transient),
7107                        environment: Some("myenv".to_string()),
7108                        allowed_offers: None,
7109                        ..Default::default()
7110                    }
7111                ]),
7112                environments: Some(vec![
7113                    fdecl::Environment {
7114                        name: Some("myenv".to_string()),
7115                        extends: Some(fdecl::EnvironmentExtends::Realm),
7116                        runners: None,
7117                        resolvers: None,
7118                        stop_timeout_ms: None,
7119                        ..Default::default()
7120                    }
7121                ]),
7122                ..default_component_decl()
7123            },
7124        },
7125
7126        test_compile_capabilities => {
7127            features = FeatureSet::from(vec![Feature::DynamicDictionaries]),
7128            input = json!({
7129                "capabilities": [
7130                    {
7131                        "protocol": "myprotocol",
7132                        "path": "/protocol",
7133                    },
7134                    {
7135                        "protocol": "myprotocol2",
7136                    },
7137                    {
7138                        "protocol": [ "myprotocol3", "myprotocol4" ],
7139                    },
7140                    {
7141                        "directory": "mydirectory",
7142                        "path": "/directory",
7143                        "rights": [ "connect" ],
7144                    },
7145                    {
7146                        "storage": "mystorage",
7147                        "backing_dir": "storage",
7148                        "from": "#minfs",
7149                        "storage_id": "static_instance_id_or_moniker",
7150                    },
7151                    {
7152                        "storage": "mystorage2",
7153                        "backing_dir": "storage2",
7154                        "from": "#minfs",
7155                        "storage_id": "static_instance_id",
7156                    },
7157                    {
7158                        "runner": "myrunner",
7159                        "path": "/runner",
7160                    },
7161                    {
7162                        "resolver": "myresolver",
7163                        "path": "/resolver"
7164                    },
7165                    {
7166                        "dictionary": "dict1",
7167                    },
7168                    {
7169                        "dictionary": "dict2",
7170                        "path": "/in/a",
7171                    },
7172                ],
7173                "children": [
7174                    {
7175                        "name": "minfs",
7176                        "url": "fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm",
7177                    },
7178                ]
7179            }),
7180            output = fdecl::Component {
7181                capabilities: Some(vec![
7182                    fdecl::Capability::Protocol (
7183                        fdecl::Protocol {
7184                            name: Some("myprotocol".to_string()),
7185                            source_path: Some("/protocol".to_string()),
7186                            ..Default::default()
7187                        }
7188                    ),
7189                    fdecl::Capability::Protocol (
7190                        fdecl::Protocol {
7191                            name: Some("myprotocol2".to_string()),
7192                            source_path: Some("/svc/myprotocol2".to_string()),
7193                            ..Default::default()
7194                        }
7195                    ),
7196                    fdecl::Capability::Protocol (
7197                        fdecl::Protocol {
7198                            name: Some("myprotocol3".to_string()),
7199                            source_path: Some("/svc/myprotocol3".to_string()),
7200                            ..Default::default()
7201                        }
7202                    ),
7203                    fdecl::Capability::Protocol (
7204                        fdecl::Protocol {
7205                            name: Some("myprotocol4".to_string()),
7206                            source_path: Some("/svc/myprotocol4".to_string()),
7207                            ..Default::default()
7208                        }
7209                    ),
7210                    fdecl::Capability::Directory (
7211                        fdecl::Directory {
7212                            name: Some("mydirectory".to_string()),
7213                            source_path: Some("/directory".to_string()),
7214                            rights: Some(fio::Operations::CONNECT),
7215                            ..Default::default()
7216                        }
7217                    ),
7218                    fdecl::Capability::Storage (
7219                        fdecl::Storage {
7220                            name: Some("mystorage".to_string()),
7221                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7222                                name: "minfs".to_string(),
7223                                collection: None,
7224                            })),
7225                            backing_dir: Some("storage".to_string()),
7226                            subdir: None,
7227                            storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7228                            ..Default::default()
7229                        }
7230                    ),
7231                    fdecl::Capability::Storage (
7232                        fdecl::Storage {
7233                            name: Some("mystorage2".to_string()),
7234                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7235                                name: "minfs".to_string(),
7236                                collection: None,
7237                            })),
7238                            backing_dir: Some("storage2".to_string()),
7239                            subdir: None,
7240                            storage_id: Some(fdecl::StorageId::StaticInstanceId),
7241                            ..Default::default()
7242                        }
7243                    ),
7244                    fdecl::Capability::Runner (
7245                        fdecl::Runner {
7246                            name: Some("myrunner".to_string()),
7247                            source_path: Some("/runner".to_string()),
7248                            ..Default::default()
7249                        }
7250                    ),
7251                    fdecl::Capability::Resolver (
7252                        fdecl::Resolver {
7253                            name: Some("myresolver".to_string()),
7254                            source_path: Some("/resolver".to_string()),
7255                            ..Default::default()
7256                        }
7257                    ),
7258                    fdecl::Capability::Dictionary (
7259                        fdecl::Dictionary {
7260                            name: Some("dict1".into()),
7261                            ..Default::default()
7262                        }
7263                    ),
7264                    fdecl::Capability::Dictionary (
7265                        fdecl::Dictionary {
7266                            name: Some("dict2".into()),
7267                            source: None,
7268                            source_dictionary: None,
7269                            source_path: Some("/in/a".into()),
7270                            ..Default::default()
7271                        }
7272                    ),
7273                ]),
7274                children: Some(vec![
7275                    fdecl::Child {
7276                        name: Some("minfs".to_string()),
7277                        url: Some("fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm".to_string()),
7278                        startup: Some(fdecl::StartupMode::Lazy),
7279                        environment: None,
7280                        on_terminate: None,
7281                        ..Default::default()
7282                    }
7283                ]),
7284                ..default_component_decl()
7285            },
7286        },
7287
7288        test_compile_facets => {
7289            input = json!({
7290                "facets": {
7291                    "title": "foo",
7292                    "authors": [ "me", "you" ],
7293                    "year": "2018",
7294                    "metadata": {
7295                        "publisher": "The Books Publisher",
7296                    }
7297                }
7298            }),
7299            output = fdecl::Component {
7300                facets: Some(fdata::Dictionary {
7301                        entries: Some(vec![
7302                            fdata::DictionaryEntry {
7303                                key: "authors".to_string(),
7304                                value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["me".to_owned(), "you".to_owned()]))),
7305                            },
7306                            fdata::DictionaryEntry {
7307                                key: "metadata.publisher".to_string(),
7308                                value: Some(Box::new(fdata::DictionaryValue::Str("The Books Publisher".to_string()))),
7309                            },
7310                            fdata::DictionaryEntry {
7311                                key: "title".to_string(),
7312                                value: Some(Box::new(fdata::DictionaryValue::Str("foo".to_string()))),
7313                            },
7314                            fdata::DictionaryEntry {
7315                                key: "year".to_string(),
7316                                value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
7317                            },
7318                        ]),
7319                        ..Default::default()
7320                    }
7321            ),
7322            ..default_component_decl()
7323            },
7324        },
7325
7326        test_compile_environment => {
7327            input = json!({
7328                "environments": [
7329                    {
7330                        "name": "myenv",
7331                        "__stop_timeout_ms": 10u32,
7332                    },
7333                    {
7334                        "name": "myenv2",
7335                        "extends": "realm",
7336                    },
7337                    {
7338                        "name": "myenv3",
7339                        "extends": "none",
7340                        "__stop_timeout_ms": 8000u32,
7341                    }
7342                ],
7343            }),
7344            output = fdecl::Component {
7345                environments: Some(vec![
7346                    fdecl::Environment {
7347                        name: Some("myenv".to_string()),
7348                        extends: Some(fdecl::EnvironmentExtends::None),
7349                        runners: None,
7350                        resolvers: None,
7351                        stop_timeout_ms: Some(10),
7352                        ..Default::default()
7353                    },
7354                    fdecl::Environment {
7355                        name: Some("myenv2".to_string()),
7356                        extends: Some(fdecl::EnvironmentExtends::Realm),
7357                        runners: None,
7358                        resolvers: None,
7359                        stop_timeout_ms: None,
7360                        ..Default::default()
7361                    },
7362                    fdecl::Environment {
7363                        name: Some("myenv3".to_string()),
7364                        extends: Some(fdecl::EnvironmentExtends::None),
7365                        runners: None,
7366                        resolvers: None,
7367                        stop_timeout_ms: Some(8000),
7368                        ..Default::default()
7369                    },
7370                ]),
7371                ..default_component_decl()
7372            },
7373        },
7374
7375        test_compile_environment_with_runner_and_resolver => {
7376            input = json!({
7377                "environments": [
7378                    {
7379                        "name": "myenv",
7380                        "extends": "realm",
7381                        "runners": [
7382                            {
7383                                "runner": "dart",
7384                                "from": "parent",
7385                            }
7386                        ],
7387                        "resolvers": [
7388                            {
7389                                "resolver": "pkg_resolver",
7390                                "from": "parent",
7391                                "scheme": "fuchsia-pkg",
7392                            }
7393                        ],
7394                    },
7395                ],
7396            }),
7397            output = fdecl::Component {
7398                environments: Some(vec![
7399                    fdecl::Environment {
7400                        name: Some("myenv".to_string()),
7401                        extends: Some(fdecl::EnvironmentExtends::Realm),
7402                        runners: Some(vec![
7403                            fdecl::RunnerRegistration {
7404                                source_name: Some("dart".to_string()),
7405                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7406                                target_name: Some("dart".to_string()),
7407                                ..Default::default()
7408                            }
7409                        ]),
7410                        resolvers: Some(vec![
7411                            fdecl::ResolverRegistration {
7412                                resolver: Some("pkg_resolver".to_string()),
7413                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7414                                scheme: Some("fuchsia-pkg".to_string()),
7415                                ..Default::default()
7416                            }
7417                        ]),
7418                        stop_timeout_ms: None,
7419                        ..Default::default()
7420                    },
7421                ]),
7422                ..default_component_decl()
7423            },
7424        },
7425
7426        test_compile_environment_with_runner_alias => {
7427            input = json!({
7428                "environments": [
7429                    {
7430                        "name": "myenv",
7431                        "extends": "realm",
7432                        "runners": [
7433                            {
7434                                "runner": "dart",
7435                                "from": "parent",
7436                                "as": "my-dart",
7437                            }
7438                        ],
7439                    },
7440                ],
7441            }),
7442            output = fdecl::Component {
7443                environments: Some(vec![
7444                    fdecl::Environment {
7445                        name: Some("myenv".to_string()),
7446                        extends: Some(fdecl::EnvironmentExtends::Realm),
7447                        runners: Some(vec![
7448                            fdecl::RunnerRegistration {
7449                                source_name: Some("dart".to_string()),
7450                                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7451                                target_name: Some("my-dart".to_string()),
7452                                ..Default::default()
7453                            }
7454                        ]),
7455                        resolvers: None,
7456                        stop_timeout_ms: None,
7457                        ..Default::default()
7458                    },
7459                ]),
7460                ..default_component_decl()
7461            },
7462        },
7463
7464        test_compile_environment_with_debug => {
7465            input = json!({
7466                "capabilities": [
7467                    {
7468                        "protocol": "fuchsia.serve.service",
7469                    },
7470                ],
7471                "environments": [
7472                    {
7473                        "name": "myenv",
7474                        "extends": "realm",
7475                        "debug": [
7476                            {
7477                                "protocol": "fuchsia.serve.service",
7478                                "from": "self",
7479                                "as": "my-service",
7480                            }
7481                        ],
7482                    },
7483                ],
7484            }),
7485            output = fdecl::Component {
7486                capabilities: Some(vec![
7487                    fdecl::Capability::Protocol(
7488                        fdecl::Protocol {
7489                            name : Some("fuchsia.serve.service".to_owned()),
7490                            source_path: Some("/svc/fuchsia.serve.service".to_owned()),
7491                            ..Default::default()
7492                        }
7493                    )
7494                ]),
7495                environments: Some(vec![
7496                    fdecl::Environment {
7497                        name: Some("myenv".to_string()),
7498                        extends: Some(fdecl::EnvironmentExtends::Realm),
7499                        debug_capabilities: Some(vec![
7500                            fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
7501                                source_name: Some("fuchsia.serve.service".to_string()),
7502                                source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
7503                                target_name: Some("my-service".to_string()),
7504                                ..Default::default()
7505                            }),
7506                        ]),
7507                        resolvers: None,
7508                        runners: None,
7509                        stop_timeout_ms: None,
7510                        ..Default::default()
7511                    },
7512                ]),
7513                ..default_component_decl()
7514            },
7515        },
7516
7517
7518        test_compile_configuration_capability => {
7519            input = json!({
7520                "capabilities": [
7521                    {
7522                        "config": "fuchsia.config.true",
7523                        "type": "bool",
7524                        "value": true,
7525                    },
7526                    {
7527                        "config": "fuchsia.config.false",
7528                        "type": "bool",
7529                        "value": false,
7530                    },
7531                ],
7532            }),
7533            output = fdecl::Component {
7534                capabilities: Some(vec![
7535                    fdecl::Capability::Config (
7536                        fdecl::Configuration {
7537                            name: Some("fuchsia.config.true".to_string()),
7538                            value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
7539                            ..Default::default()
7540                        }),
7541                    fdecl::Capability::Config (
7542                        fdecl::Configuration {
7543                            name: Some("fuchsia.config.false".to_string()),
7544                            value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(false))),
7545                            ..Default::default()
7546                        }),
7547                ]),
7548                ..default_component_decl()
7549            },
7550        },
7551
7552        test_compile_all_sections => {
7553            input = json!({
7554                "program": {
7555                    "runner": "elf",
7556                    "binary": "bin/app",
7557                },
7558                "use": [
7559                    { "protocol": "LegacyCoolFonts", "path": "/svc/fuchsia.fonts.LegacyProvider" },
7560                    { "protocol": [ "ReallyGoodFonts", "IWouldNeverUseTheseFonts"]},
7561                    { "protocol":  "DebugProtocol", "from": "debug"},
7562                ],
7563                "expose": [
7564                    { "directory": "blobfs", "from": "self", "rights": ["r*"]},
7565                ],
7566                "offer": [
7567                    {
7568                        "protocol": "fuchsia.logger.LegacyLog",
7569                        "from": "#logger",
7570                        "to": [ "#netstack", "#modular" ],
7571                        "dependency": "weak"
7572                    },
7573                ],
7574                "children": [
7575                    {
7576                        "name": "logger",
7577                        "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
7578                    },
7579                    {
7580                        "name": "netstack",
7581                        "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm",
7582                    },
7583                ],
7584                "collections": [
7585                    {
7586                        "name": "modular",
7587                        "durability": "transient",
7588                    },
7589                ],
7590                "capabilities": [
7591                    {
7592                        "directory": "blobfs",
7593                        "path": "/volumes/blobfs",
7594                        "rights": [ "r*" ],
7595                    },
7596                    {
7597                        "runner": "myrunner",
7598                        "path": "/runner",
7599                    },
7600                    {
7601                        "protocol": "fuchsia.serve.service",
7602                    }
7603                ],
7604                "facets": {
7605                    "author": "Fuchsia",
7606                    "year": "2018",
7607                },
7608                "environments": [
7609                    {
7610                        "name": "myenv",
7611                        "extends": "realm",
7612                        "debug": [
7613                            {
7614                                "protocol": "fuchsia.serve.service",
7615                                "from": "self",
7616                                "as": "my-service",
7617                            },
7618                            {
7619                                "protocol": "fuchsia.logger.LegacyLog",
7620                                "from": "#logger",
7621                            }
7622                        ]
7623                    }
7624                ],
7625            }),
7626            output = fdecl::Component {
7627                program: Some(fdecl::Program {
7628                    runner: Some("elf".to_string()),
7629                    info: Some(fdata::Dictionary {
7630                        entries: Some(vec![fdata::DictionaryEntry {
7631                            key: "binary".to_string(),
7632                            value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
7633                        }]),
7634                        ..Default::default()
7635                    }),
7636                    ..Default::default()
7637                }),
7638                uses: Some(vec![
7639                    fdecl::Use::Protocol (
7640                        fdecl::UseProtocol {
7641                            dependency_type: Some(fdecl::DependencyType::Strong),
7642                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7643                            source_name: Some("LegacyCoolFonts".to_string()),
7644                            target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
7645                            availability: Some(fdecl::Availability::Required),
7646                            ..Default::default()
7647                        }
7648                    ),
7649                    fdecl::Use::Protocol (
7650                        fdecl::UseProtocol {
7651                            dependency_type: Some(fdecl::DependencyType::Strong),
7652                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7653                            source_name: Some("ReallyGoodFonts".to_string()),
7654                            target_path: Some("/svc/ReallyGoodFonts".to_string()),
7655                            availability: Some(fdecl::Availability::Required),
7656                            ..Default::default()
7657                        }
7658                    ),
7659                    fdecl::Use::Protocol (
7660                        fdecl::UseProtocol {
7661                            dependency_type: Some(fdecl::DependencyType::Strong),
7662                            source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7663                            source_name: Some("IWouldNeverUseTheseFonts".to_string()),
7664                            target_path: Some("/svc/IWouldNeverUseTheseFonts".to_string()),
7665                            availability: Some(fdecl::Availability::Required),
7666                            ..Default::default()
7667                        }
7668                    ),
7669                    fdecl::Use::Protocol (
7670                        fdecl::UseProtocol {
7671                            dependency_type: Some(fdecl::DependencyType::Strong),
7672                            source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
7673                            source_name: Some("DebugProtocol".to_string()),
7674                            target_path: Some("/svc/DebugProtocol".to_string()),
7675                            availability: Some(fdecl::Availability::Required),
7676                            ..Default::default()
7677                        }
7678                    ),
7679                ]),
7680                exposes: Some(vec![
7681                    fdecl::Expose::Directory (
7682                        fdecl::ExposeDirectory {
7683                            source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
7684                            source_name: Some("blobfs".to_string()),
7685                            target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7686                            target_name: Some("blobfs".to_string()),
7687                            rights: Some(
7688                                fio::Operations::CONNECT | fio::Operations::ENUMERATE |
7689                                fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
7690                                fio::Operations::GET_ATTRIBUTES
7691                            ),
7692                            subdir: None,
7693                            availability: Some(fdecl::Availability::Required),
7694                            ..Default::default()
7695                        }
7696                    ),
7697                ]),
7698                offers: Some(vec![
7699                    fdecl::Offer::Protocol (
7700                        fdecl::OfferProtocol {
7701                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7702                                name: "logger".to_string(),
7703                                collection: None,
7704                            })),
7705                            source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7706                            target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7707                                name: "netstack".to_string(),
7708                                collection: None,
7709                            })),
7710                            target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7711                            dependency_type: Some(fdecl::DependencyType::Weak),
7712                            availability: Some(fdecl::Availability::Required),
7713                            ..Default::default()
7714                        }
7715                    ),
7716                    fdecl::Offer::Protocol (
7717                        fdecl::OfferProtocol {
7718                            source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7719                                name: "logger".to_string(),
7720                                collection: None,
7721                            })),
7722                            source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7723                            target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7724                                name: "modular".to_string(),
7725                            })),
7726                            target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7727                            dependency_type: Some(fdecl::DependencyType::Weak),
7728                            availability: Some(fdecl::Availability::Required),
7729                            ..Default::default()
7730                        }
7731                    ),
7732                ]),
7733                capabilities: Some(vec![
7734                    fdecl::Capability::Directory (
7735                        fdecl::Directory {
7736                            name: Some("blobfs".to_string()),
7737                            source_path: Some("/volumes/blobfs".to_string()),
7738                            rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
7739                                fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
7740                                fio::Operations::GET_ATTRIBUTES
7741                            ),
7742                            ..Default::default()
7743                        }
7744                    ),
7745                    fdecl::Capability::Runner (
7746                        fdecl::Runner {
7747                            name: Some("myrunner".to_string()),
7748                            source_path: Some("/runner".to_string()),
7749                            ..Default::default()
7750                        }
7751                    ),
7752                    fdecl::Capability::Protocol(
7753                        fdecl::Protocol {
7754                            name : Some("fuchsia.serve.service".to_owned()),
7755                            source_path: Some("/svc/fuchsia.serve.service".to_owned()),
7756                            ..Default::default()
7757                        }
7758                    )
7759                ]),
7760                children: Some(vec![
7761                    fdecl::Child {
7762                        name: Some("logger".to_string()),
7763                        url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7764                        startup: Some(fdecl::StartupMode::Lazy),
7765                        environment: None,
7766                        on_terminate: None,
7767                        ..Default::default()
7768                    },
7769                    fdecl::Child {
7770                        name: Some("netstack".to_string()),
7771                        url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7772                        startup: Some(fdecl::StartupMode::Lazy),
7773                        environment: None,
7774                        on_terminate: None,
7775                        ..Default::default()
7776                    },
7777                ]),
7778                collections: Some(vec![
7779                    fdecl::Collection {
7780                        name: Some("modular".to_string()),
7781                        durability: Some(fdecl::Durability::Transient),
7782                        environment: None,
7783                        allowed_offers: None,
7784                        ..Default::default()
7785                    }
7786                ]),
7787                environments: Some(vec![
7788                    fdecl::Environment {
7789                        name: Some("myenv".to_string()),
7790                        extends: Some(fdecl::EnvironmentExtends::Realm),
7791                        runners: None,
7792                        resolvers: None,
7793                        stop_timeout_ms: None,
7794                        debug_capabilities: Some(vec![
7795                            fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
7796                                source_name: Some("fuchsia.serve.service".to_string()),
7797                                source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
7798                                target_name: Some("my-service".to_string()),
7799                                ..Default::default()
7800                            }),
7801                            fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
7802                                source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7803                                source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7804                                    name: "logger".to_string(),
7805                                    collection: None,
7806                                })),
7807                                target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7808                                ..Default::default()
7809                            }),
7810                        ]),
7811                        ..Default::default()
7812                    }
7813                ]),
7814                facets: Some(fdata::Dictionary {
7815                        entries: Some(vec![
7816                            fdata::DictionaryEntry {
7817                                key: "author".to_string(),
7818                                value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
7819                            },
7820                            fdata::DictionaryEntry {
7821                                key: "year".to_string(),
7822                                value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
7823                            },
7824                        ]),
7825                        ..Default::default()
7826                    }),
7827                ..Default::default()
7828            },
7829        },
7830    }
7831
7832    #[test]
7833    fn test_maybe_generate_specialization_from_all() {
7834        let offer = create_offer(
7835            "fuchsia.logger.LegacyLog",
7836            OneOrMany::One(OfferFromRef::Parent {}),
7837            OneOrMany::One(OfferToRef::All),
7838        );
7839
7840        let mut offer_set = vec![create_offer(
7841            "fuchsia.logger.LogSink",
7842            OneOrMany::One(OfferFromRef::Parent {}),
7843            OneOrMany::One(OfferToRef::All),
7844        )];
7845
7846        let result = maybe_generate_direct_offer_from_all_deprecated(
7847            &offer,
7848            &offer_set,
7849            &Name::from_str("something").unwrap(),
7850        );
7851
7852        assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
7853            assert_eq!(
7854                protocol,
7855                &Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
7856            );
7857            assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
7858            assert_eq!(
7859                to,
7860                &OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7861            );
7862        });
7863
7864        offer_set.push(create_offer(
7865            "fuchsia.inspect.InspectSink",
7866            OneOrMany::One(OfferFromRef::Parent {}),
7867            OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7868        ));
7869
7870        let result = maybe_generate_direct_offer_from_all_deprecated(
7871            &offer,
7872            &offer_set,
7873            &Name::from_str("something").unwrap(),
7874        );
7875
7876        assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
7877            assert_eq!(
7878                protocol,
7879                &Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
7880            );
7881            assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
7882            assert_eq!(
7883                to,
7884                &OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7885            );
7886        });
7887
7888        offer_set.push(create_offer(
7889            "fuchsia.logger.LegacyLog",
7890            OneOrMany::One(OfferFromRef::Parent {}),
7891            OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7892        ));
7893
7894        assert!(
7895            maybe_generate_direct_offer_from_all_deprecated(
7896                &offer,
7897                &offer_set,
7898                &Name::from_str("something").unwrap()
7899            )
7900            .is_empty()
7901        );
7902    }
7903
7904    #[test]
7905    fn test_expose_void_service_capability() {
7906        let input = must_parse_cml!({
7907            "expose": [
7908                {
7909                    "service": "fuchsia.foo.Bar",
7910                    "from": [ "#non_existent_child" ],
7911                    "source_availability": "unknown",
7912                },
7913            ],
7914        });
7915        let result = compile(&input, CompileOptions::default());
7916        assert_matches!(result, Ok(_));
7917    }
7918
7919    /// Different availabilities aggregated by several service expose declarations is an error.
7920    #[test]
7921    fn test_aggregated_capabilities_must_use_same_availability_expose() {
7922        // Same availability.
7923        let input = must_parse_cml!({
7924            "expose": [
7925                {
7926                    "service": "fuchsia.foo.Bar",
7927                    "from": [ "#a", "#b" ],
7928                    "availability": "optional",
7929                },
7930            ],
7931            "collections": [
7932                {
7933                    "name": "a",
7934                    "durability": "transient",
7935                },
7936                {
7937                    "name": "b",
7938                    "durability": "transient",
7939                },
7940            ],
7941        });
7942        let result = compile(&input, CompileOptions::default());
7943        assert_matches!(result, Ok(_));
7944
7945        // Different availability.
7946        let input = must_parse_cml!({
7947            "expose": [
7948                {
7949                    "service": "fuchsia.foo.Bar",
7950                    "from": [ "#a", "#non_existent" ],
7951                    "source_availability": "unknown",
7952                },
7953            ],
7954            "collections": [
7955                {
7956                    "name": "a",
7957                    "durability": "transient",
7958                },
7959            ],
7960        });
7961        let result = compile(&input, CompileOptions::default());
7962        assert_matches!(
7963            result,
7964            Err(Error::FidlValidator  { errs: ErrorList { errs } })
7965            if matches!(
7966                &errs[..],
7967                [
7968                    CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
7969                ]
7970                if matches!(
7971                    &availabilities[..],
7972                    [ fdecl::Availability::Required, fdecl::Availability::Optional, ]
7973                )
7974            )
7975        );
7976    }
7977
7978    #[test]
7979    fn test_aggregated_capabilities_must_use_same_availability_offer() {
7980        // Same availability.
7981        let input = must_parse_cml!({
7982            "offer": [
7983                {
7984                    "service": "fuchsia.foo.Bar",
7985                    "from": [ "#a", "#b" ],
7986                    "to": "#c",
7987                    "availability": "optional",
7988                },
7989            ],
7990            "collections": [
7991                {
7992                    "name": "a",
7993                    "durability": "transient",
7994                },
7995                {
7996                    "name": "b",
7997                    "durability": "transient",
7998                },
7999            ],
8000            "children": [
8001                {
8002                    "name": "c",
8003                    "url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
8004                },
8005            ],
8006        });
8007        let result = compile(&input, CompileOptions::default());
8008        assert_matches!(result, Ok(_));
8009
8010        // Different availability.
8011        let input = must_parse_cml!({
8012            "offer": [
8013                {
8014                    "service": "fuchsia.foo.Bar",
8015                    "from": [ "#a", "#non_existent" ],
8016                    "to": "#c",
8017                    "source_availability": "unknown",
8018                },
8019            ],
8020            "collections": [
8021                {
8022                    "name": "a",
8023                    "durability": "transient",
8024                },
8025            ],
8026            "children": [
8027                {
8028                    "name": "c",
8029                    "url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
8030                },
8031            ],
8032        });
8033        let result = compile(&input, CompileOptions::default());
8034        assert_matches!(
8035            result,
8036            Err(Error::FidlValidator  { errs: ErrorList { errs } })
8037            if matches!(
8038                &errs[..],
8039                [
8040                    CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
8041                ]
8042                if matches!(
8043                    &availabilities[..],
8044                    [ fdecl::Availability::Required, fdecl::Availability::Optional, ]
8045                )
8046            )
8047        );
8048    }
8049
8050    #[test]
8051    fn test_compile_offer_to_all_exact_duplicate_disallowed() {
8052        let input = must_parse_cml!({
8053            "children": [
8054                {
8055                    "name": "logger",
8056                    "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
8057                },
8058            ],
8059            "offer": [
8060                {
8061                    "protocol": "fuchsia.logger.LogSink",
8062                    "from": "parent",
8063                    "to": "all",
8064                },
8065                {
8066                    "protocol": "fuchsia.logger.LogSink",
8067                    "from": "parent",
8068                    "to": "all",
8069                },
8070            ],
8071        });
8072        assert_matches!(
8073            compile(&input, CompileOptions::default()),
8074            Err(Error::Validate { err, .. })
8075            if &err == "Protocol(s) [\"fuchsia.logger.LogSink\"] offered to \"all\" multiple times"
8076        );
8077    }
8078
8079    #[test]
8080    fn test_compile_use_config() {
8081        let input = must_parse_cml!({
8082            "use": [
8083                    {
8084                        "config": "fuchsia.config.Config",
8085                        "key" : "my_config",
8086                        "type": "bool",
8087                    }
8088            ],
8089        });
8090        let options = CompileOptions::new().config_package_path("fake.cvf");
8091        let actual = compile(&input, options).unwrap();
8092        let type_ = fdecl::ConfigType {
8093            layout: fdecl::ConfigTypeLayout::Bool,
8094            parameters: Some(vec![]),
8095            constraints: vec![],
8096        };
8097        assert_eq!(
8098            actual.uses.unwrap(),
8099            vec![fdecl::Use::Config(fdecl::UseConfiguration {
8100                source_name: Some("fuchsia.config.Config".to_string()),
8101                source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8102                target_name: Some("my_config".to_string()),
8103                availability: Some(fdecl::Availability::Required),
8104                type_: Some(type_.clone()),
8105                ..Default::default()
8106            })]
8107        );
8108        assert_eq!(
8109            actual.config.unwrap().fields.unwrap(),
8110            [fdecl::ConfigField {
8111                key: Some("my_config".to_string()),
8112                type_: Some(type_),
8113                mutability: Some(fdecl::ConfigMutability::default()),
8114                ..Default::default()
8115            }]
8116            .to_vec(),
8117        );
8118    }
8119
8120    #[test]
8121    fn test_compile_use_config_optional_bad_type() {
8122        let input = must_parse_cml!({
8123            "use": [
8124                    {
8125                        "config": "fuchsia.config.Config",
8126                        "key" : "my_config",
8127                        "type": "bool",
8128                        "availability": "optional",
8129                    }
8130            ],
8131        "config": {
8132            "my_config": { "type": "int8"},
8133        }
8134        });
8135        let options = CompileOptions::new().config_package_path("fake.cvf");
8136        assert_matches!(
8137            compile(&input, options),
8138            Err(Error::Validate { err, .. })
8139            if &err == "Use and config block differ on type for key 'my_config'"
8140        );
8141    }
8142
8143    #[test]
8144    fn test_config_source_from_package() {
8145        let input = must_parse_cml!({
8146            "use": [
8147                    {
8148                        "config": "fuchsia.config.Config",
8149                        "key" : "my_config",
8150                        "type": "bool",
8151                        "availability": "optional",
8152                    }
8153            ],
8154        "config": {
8155            "my_config": { "type": "bool"},
8156        }
8157        });
8158        let options = CompileOptions::new().config_package_path("fake.cvf");
8159        let decl = compile(&input, options).unwrap();
8160        let config = decl.config.unwrap();
8161        assert_eq!(
8162            config.value_source,
8163            Some(fdecl::ConfigValueSource::PackagePath("fake.cvf".into()))
8164        );
8165    }
8166
8167    #[test]
8168    fn test_config_source_from_capabilities() {
8169        let input = must_parse_cml!({
8170            "use": [
8171                    {
8172                        "config": "fuchsia.config.Config",
8173                        "key" : "my_config",
8174                        "type": "bool",
8175                    }
8176            ],
8177        });
8178        let options = CompileOptions::new().config_package_path("fake.cvf");
8179        let decl = compile(&input, options).unwrap();
8180        let config = decl.config.unwrap();
8181        assert_eq!(
8182            config.value_source,
8183            Some(
8184                fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default())
8185            )
8186        );
8187    }
8188
8189    #[test]
8190    fn test_config_default() {
8191        let input = must_parse_cml!({
8192            "use": [
8193                    {
8194                        "config": "fuchsia.config.Config",
8195                        "key" : "my_config",
8196                        "type": "bool",
8197                        "availability": "optional",
8198                        "default": true
8199                    }
8200            ],
8201        });
8202        let options = CompileOptions::new().config_package_path("fake.cvf");
8203        let decl = compile(&input, options).unwrap();
8204        assert_matches!(
8205            decl.uses.as_ref().unwrap()[0],
8206            fdecl::Use::Config(fdecl::UseConfiguration {
8207                default: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
8208                ..
8209            })
8210        );
8211    }
8212
8213    #[test]
8214    fn test_config_default_bad_type() {
8215        let input = must_parse_cml!({
8216            "use": [
8217                    {
8218                        "config": "fuchsia.config.Config",
8219                        "key" : "my_config",
8220                        "type": "bool",
8221                        "availability": "optional",
8222                        "default": 5
8223                    }
8224            ],
8225        });
8226        let options = CompileOptions::new().config_package_path("fake.cvf");
8227        assert_matches!(compile(&input, options), Err(Error::InvalidArgs(_)));
8228    }
8229
8230    #[test]
8231    fn test_compile_protocol_delivery_type() {
8232        let input = must_parse_cml!({
8233            "capabilities": [
8234                {
8235                    "protocol": "fuchsia.echo.Echo",
8236                    "delivery": "on_readable",
8237                }
8238            ],
8239        });
8240        let features = FeatureSet::from(vec![Feature::DeliveryType]);
8241        let options = CompileOptions::new().features(&features);
8242        let decl = compile(&input, options).unwrap();
8243        assert_matches!(
8244            decl.capabilities.as_ref().unwrap()[0],
8245            fdecl::Capability::Protocol(fdecl::Protocol {
8246                delivery: Some(fdecl::DeliveryType::OnReadable),
8247                ..
8248            })
8249        );
8250    }
8251
8252    #[test]
8253    fn test_compile_protocol_setting_delivery_type_requires_feature_flag() {
8254        let input = must_parse_cml!({
8255            "capabilities": [
8256                {
8257                    "protocol": "fuchsia.echo.Echo",
8258                    "delivery": "on_readable",
8259                }
8260            ],
8261        });
8262        assert_matches!(
8263            compile(&input, CompileOptions::new()),
8264            Err(Error::RestrictedFeature(feature))
8265            if feature == "delivery_type"
8266        );
8267    }
8268}