1use crate::error::Error;
6use crate::features::{Feature, FeatureSet};
7use crate::validate::CapabilityRequirements;
8use crate::{
9 offer_to_all_would_duplicate, validate, AnyRef, AsClause, Availability, Capability,
10 CapabilityClause, Child, Collection, ConfigKey, ConfigNestedValueType, ConfigRuntimeSource,
11 ConfigType, ConfigValueType, DebugRegistration, DictionaryRef, Document, Environment,
12 EnvironmentExtends, EnvironmentRef, EventScope, Expose, ExposeFromRef, ExposeToRef, FromClause,
13 Offer, OfferFromRef, OfferToRef, OneOrMany, Path, PathClause, Program, ResolverRegistration,
14 RightsClause, RootDictionaryRef, RunnerRegistration, SourceAvailability, Use, UseFromRef,
15};
16use cm_rust::NativeIntoFidl;
17use cm_types::{self as cm, Name};
18use indexmap::IndexMap;
19use itertools::Itertools;
20use serde_json::{Map, Value};
21use sha2::{Digest, Sha256};
22use std::collections::{BTreeMap, BTreeSet};
23use std::convert::{Into, TryInto};
24use std::path::PathBuf;
25use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio};
26
27#[derive(Default, Clone)]
29pub struct CompileOptions<'a> {
30 file: Option<PathBuf>,
31 config_package_path: Option<String>,
32 features: Option<&'a FeatureSet>,
33 capability_requirements: CapabilityRequirements<'a>,
34}
35
36impl<'a> CompileOptions<'a> {
37 pub fn new() -> Self {
38 Default::default()
39 }
40
41 pub fn file(mut self, file: &std::path::Path) -> CompileOptions<'a> {
43 self.file = Some(file.to_path_buf());
44 self
45 }
46
47 pub fn config_package_path(mut self, config_package_path: &str) -> CompileOptions<'a> {
49 self.config_package_path = Some(config_package_path.to_string());
50 self
51 }
52
53 pub fn features(mut self, features: &'a FeatureSet) -> CompileOptions<'a> {
55 self.features = Some(features);
56 self
57 }
58
59 pub fn protocol_requirements(
62 mut self,
63 protocol_requirements: CapabilityRequirements<'a>,
64 ) -> CompileOptions<'a> {
65 self.capability_requirements = protocol_requirements;
66 self
67 }
68}
69
70pub fn compile(
77 document: &Document,
78 options: CompileOptions<'_>,
79) -> Result<fdecl::Component, Error> {
80 validate::validate_cml(
81 &document,
82 options.file.as_ref().map(PathBuf::as_path),
83 options.features.unwrap_or(&FeatureSet::empty()),
84 &options.capability_requirements,
85 )?;
86
87 let all_capability_names: BTreeSet<&Name> =
88 document.all_capability_names().into_iter().collect();
89 let all_children = document.all_children_names().into_iter().collect();
90 let all_collections = document.all_collection_names().into_iter().collect();
91 let component = fdecl::Component {
92 program: document.program.as_ref().map(translate_program).transpose()?,
93 uses: document
94 .r#use
95 .as_ref()
96 .map(|u| {
97 translate_use(&options, u, &all_capability_names, &all_children, &all_collections)
98 })
99 .transpose()?,
100 exposes: document
101 .expose
102 .as_ref()
103 .map(|e| {
104 translate_expose(
105 &options,
106 e,
107 &all_capability_names,
108 &all_collections,
109 &all_children,
110 )
111 })
112 .transpose()?,
113 offers: document
114 .offer
115 .as_ref()
116 .map(|offer| {
117 translate_offer(
118 &options,
119 offer,
120 &all_capability_names,
121 &all_children,
122 &all_collections,
123 )
124 })
125 .transpose()?,
126 capabilities: document
127 .capabilities
128 .as_ref()
129 .map(|c| translate_capabilities(&options, c, false))
130 .transpose()?,
131 children: document.children.as_ref().map(translate_children).transpose()?,
132 collections: document.collections.as_ref().map(translate_collections).transpose()?,
133 environments: document
134 .environments
135 .as_ref()
136 .map(|env| translate_environments(&options, env, &all_capability_names))
137 .transpose()?,
138 facets: document.facets.clone().map(dictionary_from_nested_map).transpose()?,
139 config: translate_config(&document.config, &document.r#use, &options.config_package_path)?,
140 ..Default::default()
141 };
142
143 cm_fidl_validator::validate(&component).map_err(Error::fidl_validator)?;
144
145 Ok(component)
146}
147
148fn dictionary_from_map(in_obj: Map<String, Value>) -> Result<fdata::Dictionary, Error> {
150 let mut entries = vec![];
151 for (key, v) in in_obj {
152 let value = value_to_dictionary_value(v)?;
153 entries.push(fdata::DictionaryEntry { key, value });
154 }
155 Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
156}
157
158fn value_to_dictionary_value(value: Value) -> Result<Option<Box<fdata::DictionaryValue>>, Error> {
160 match value {
161 Value::Null => Ok(None),
162 Value::String(s) => Ok(Some(Box::new(fdata::DictionaryValue::Str(s)))),
163 Value::Array(arr) => {
164 if arr.iter().all(Value::is_string) {
165 let strs =
166 arr.into_iter().map(|v| v.as_str().unwrap().to_owned()).collect::<Vec<_>>();
167 Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs))))
168 } else if arr.iter().all(Value::is_object) {
169 let objs = arr
170 .into_iter()
171 .map(|v| v.as_object().unwrap().clone())
172 .map(|v| dictionary_from_nested_map(v.into_iter().collect()))
173 .collect::<Result<Vec<_>, _>>()?;
174 Ok(Some(Box::new(fdata::DictionaryValue::ObjVec(objs))))
175 } else {
176 Err(Error::validate(
177 "Values of an array must either exclusively strings or exclusively objects",
178 ))
179 }
180 }
181 other => Err(Error::validate(format!(
182 "Value must be string, list of strings, or list of objects: {:?}",
183 other
184 ))),
185 }
186}
187
188fn dictionary_from_nested_map(map: IndexMap<String, Value>) -> Result<fdata::Dictionary, Error> {
223 fn key_value_to_entries(
224 key: String,
225 value: Value,
226 ) -> Result<Vec<fdata::DictionaryEntry>, Error> {
227 if let Value::Object(map) = value {
228 let entries = map
229 .into_iter()
230 .map(|(k, v)| key_value_to_entries([key.clone(), ".".to_string(), k].concat(), v))
231 .collect::<Result<Vec<_>, _>>()?
232 .into_iter()
233 .flatten()
234 .collect();
235 return Ok(entries);
236 }
237
238 let entry_value = value_to_dictionary_value(value)?;
239 Ok(vec![fdata::DictionaryEntry { key, value: entry_value }])
240 }
241
242 let entries = map
243 .into_iter()
244 .map(|(k, v)| key_value_to_entries(k, v))
245 .collect::<Result<Vec<_>, _>>()?
246 .into_iter()
247 .flatten()
248 .collect();
249 Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
250}
251
252fn translate_program(program: &Program) -> Result<fdecl::Program, Error> {
254 Ok(fdecl::Program {
255 runner: program.runner.as_ref().map(|r| r.to_string()),
256 info: Some(dictionary_from_nested_map(program.info.clone())?),
257 ..Default::default()
258 })
259}
260
261fn translate_use(
263 options: &CompileOptions<'_>,
264 use_in: &Vec<Use>,
265 all_capability_names: &BTreeSet<&Name>,
266 all_children: &BTreeSet<&Name>,
267 all_collections: &BTreeSet<&Name>,
268) -> Result<Vec<fdecl::Use>, Error> {
269 let mut out_uses = vec![];
270 for use_ in use_in {
271 if let Some(n) = use_.service() {
272 let (source, source_dictionary) = extract_use_source(
273 options,
274 use_,
275 all_capability_names,
276 all_children,
277 Some(all_collections),
278 )?;
279 let target_paths =
280 all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
281 let source_names = n.into_iter();
282 let availability = extract_use_availability(use_)?;
283 for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
284 {
285 out_uses.push(fdecl::Use::Service(fdecl::UseService {
286 source: Some(source.clone()),
287 source_name: Some(source_name.clone().into()),
288 #[cfg(fuchsia_api_level_at_least = "25")]
289 source_dictionary: source_dictionary.clone(),
290 target_path: Some(target_path.into()),
291 dependency_type: Some(
292 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
293 ),
294 availability: Some(availability),
295 ..Default::default()
296 }));
297 }
298 } else if let Some(n) = use_.protocol() {
299 let (source, source_dictionary) =
300 extract_use_source(options, use_, all_capability_names, all_children, None)?;
301 let target_paths =
302 all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
303 let source_names = n.into_iter();
304 let availability = extract_use_availability(use_)?;
305 for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
306 {
307 out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
308 source: Some(source.clone()),
309 source_name: Some(source_name.clone().into()),
310 #[cfg(fuchsia_api_level_at_least = "25")]
311 source_dictionary: source_dictionary.clone(),
312 target_path: Some(target_path.into()),
313 dependency_type: Some(
314 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
315 ),
316 availability: Some(availability),
317 ..Default::default()
318 }));
319 }
320 } else if let Some(n) = &use_.directory {
321 let (source, source_dictionary) =
322 extract_use_source(options, use_, all_capability_names, all_children, None)?;
323 let target_path = one_target_use_path(use_, use_)?;
324 let rights = extract_required_rights(use_, "use")?;
325 let subdir = extract_use_subdir(use_);
326 let availability = extract_use_availability(use_)?;
327 out_uses.push(fdecl::Use::Directory(fdecl::UseDirectory {
328 source: Some(source),
329 source_name: Some(n.clone().into()),
330 #[cfg(fuchsia_api_level_at_least = "25")]
331 source_dictionary,
332 target_path: Some(target_path.into()),
333 rights: Some(rights),
334 subdir: subdir.map(|s| s.into()),
335 dependency_type: Some(
336 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
337 ),
338 availability: Some(availability),
339 ..Default::default()
340 }));
341 } else if let Some(n) = &use_.storage {
342 let target_path = one_target_use_path(use_, use_)?;
343 let availability = extract_use_availability(use_)?;
344 out_uses.push(fdecl::Use::Storage(fdecl::UseStorage {
345 source_name: Some(n.clone().into()),
346 target_path: Some(target_path.into()),
347 availability: Some(availability),
348 ..Default::default()
349 }));
350 } else if let Some(names) = &use_.event_stream {
351 let source_names: Vec<String> =
352 annotate_type::<Vec<cm_types::Name>>(names.clone().into())
353 .iter()
354 .map(|name| name.to_string())
355 .collect();
356 let availability = extract_use_availability(use_)?;
357 for name in source_names {
358 let scopes = match use_.scope.clone() {
359 Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
360 None => None,
361 };
362 let internal_error = format!("Internal error in all_target_use_paths when translating an EventStream. Please file a bug.");
363 let (source, _source_dictionary) =
364 extract_use_source(options, use_, all_capability_names, all_children, None)?;
365 out_uses.push(fdecl::Use::EventStream(fdecl::UseEventStream {
366 source_name: Some(name),
367 scope: match scopes {
368 Some(values) => {
369 let mut output = vec![];
370 for value in &values {
371 static EMPTY_SET: BTreeSet<&Name> = BTreeSet::new();
372 output.push(translate_target_ref(
373 options,
374 value.into(),
375 &all_children,
376 &all_collections,
377 &EMPTY_SET,
378 )?);
379 }
380 Some(output)
381 }
382 None => None,
383 },
384 source: Some(source),
385 target_path: Some(
386 annotate_type::<Vec<cm_types::Path>>(
387 all_target_use_paths(use_, use_)
388 .ok_or_else(|| Error::internal(internal_error.clone()))?
389 .into(),
390 )
391 .iter()
392 .next()
393 .ok_or_else(|| Error::internal(internal_error.clone()))?
394 .to_string(),
395 ),
396 filter: match use_.filter.clone() {
397 Some(dict) => Some(dictionary_from_map(dict)?),
398 None => None,
399 },
400 availability: Some(availability),
401 ..Default::default()
402 }));
403 }
404 } else if let Some(n) = &use_.runner {
405 let (source, source_dictionary) =
406 extract_use_source(&options, use_, all_capability_names, all_children, None)?;
407 #[cfg(fuchsia_api_level_at_least = "HEAD")]
408 out_uses.push(fdecl::Use::Runner(fdecl::UseRunner {
409 source: Some(source),
410 source_name: Some(n.clone().into()),
411 source_dictionary,
412 ..Default::default()
413 }));
414 } else if let Some(n) = &use_.config {
415 let (source, source_dictionary) =
416 extract_use_source(&options, use_, all_capability_names, all_children, None)?;
417 let target = match &use_.key {
418 None => return Err(Error::validate("\"use config\" must have \"key\" field set.")),
419 Some(t) => t.clone(),
420 };
421 let availability = extract_use_availability(use_)?;
422 let type_ = validate::use_config_to_value_type(use_)?;
423
424 let default = if let Some(default) = &use_.config_default {
425 let value = config_value_file::field::config_value_from_json_value(
426 default,
427 &type_.clone().into(),
428 )
429 .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", n, e)))?;
430 Some(value.native_into_fidl())
431 } else {
432 None
433 };
434
435 #[cfg(fuchsia_api_level_at_least = "20")]
436 out_uses.push(fdecl::Use::Config(fdecl::UseConfiguration {
437 source: Some(source),
438 source_name: Some(n.clone().into()),
439 target_name: Some(target.into()),
440 availability: Some(availability),
441 type_: Some(translate_value_type(&type_).0),
442 default,
443 #[cfg(fuchsia_api_level_at_least = "25")]
444 source_dictionary,
445 ..Default::default()
446 }));
447 } else {
448 return Err(Error::internal(format!("no capability in use declaration")));
449 };
450 }
451 Ok(out_uses)
452}
453
454fn translate_expose(
457 options: &CompileOptions<'_>,
458 expose_in: &Vec<Expose>,
459 all_capability_names: &BTreeSet<&Name>,
460 all_collections: &BTreeSet<&Name>,
461 all_children: &BTreeSet<&Name>,
462) -> Result<Vec<fdecl::Expose>, Error> {
463 let mut out_exposes = vec![];
464 for expose in expose_in.iter() {
465 let target = extract_expose_target(expose)?;
466 if let Some(source_names) = expose.service() {
467 let sources = extract_all_expose_sources(options, expose, Some(all_collections))?;
470 let target_names = all_target_capability_names(expose, expose)
471 .ok_or_else(|| Error::internal("no capability"))?;
472 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
473 {
474 for (source, source_dictionary) in &sources {
475 let DerivedSourceInfo { source, source_dictionary, availability } =
476 derive_source_and_availability(
477 expose.availability.as_ref(),
478 source.clone(),
479 source_dictionary.clone(),
480 expose.source_availability.as_ref(),
481 all_capability_names,
482 all_children,
483 all_collections,
484 );
485 out_exposes.push(fdecl::Expose::Service(fdecl::ExposeService {
486 source: Some(source),
487 source_name: Some(source_name.clone().into()),
488 #[cfg(fuchsia_api_level_at_least = "25")]
489 source_dictionary,
490 target_name: Some(target_name.clone().into()),
491 target: Some(target.clone()),
492 availability: Some(availability),
493 ..Default::default()
494 }))
495 }
496 }
497 } else if let Some(n) = expose.protocol() {
498 let (source, source_dictionary) =
499 extract_single_expose_source(options, expose, Some(all_capability_names))?;
500 let source_names = n.into_iter();
501 let target_names = all_target_capability_names(expose, expose)
502 .ok_or_else(|| Error::internal("no capability"))?;
503 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
504 {
505 let DerivedSourceInfo { source, source_dictionary, availability } =
506 derive_source_and_availability(
507 expose.availability.as_ref(),
508 source.clone(),
509 source_dictionary.clone(),
510 expose.source_availability.as_ref(),
511 all_capability_names,
512 all_children,
513 all_collections,
514 );
515 out_exposes.push(fdecl::Expose::Protocol(fdecl::ExposeProtocol {
516 source: Some(source),
517 source_name: Some(source_name.clone().into()),
518 #[cfg(fuchsia_api_level_at_least = "25")]
519 source_dictionary,
520 target_name: Some(target_name.clone().into()),
521 target: Some(target.clone()),
522 availability: Some(availability),
523 ..Default::default()
524 }))
525 }
526 } else if let Some(n) = expose.directory() {
527 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
528 let source_names = n.into_iter();
529 let target_names = all_target_capability_names(expose, expose)
530 .ok_or_else(|| Error::internal("no capability"))?;
531 let rights = extract_expose_rights(expose)?;
532 let subdir = extract_expose_subdir(expose);
533 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
534 {
535 let DerivedSourceInfo { source, source_dictionary, availability } =
536 derive_source_and_availability(
537 expose.availability.as_ref(),
538 source.clone(),
539 source_dictionary.clone(),
540 expose.source_availability.as_ref(),
541 all_capability_names,
542 all_children,
543 all_collections,
544 );
545 out_exposes.push(fdecl::Expose::Directory(fdecl::ExposeDirectory {
546 source: Some(source),
547 source_name: Some(source_name.clone().into()),
548 #[cfg(fuchsia_api_level_at_least = "25")]
549 source_dictionary,
550 target_name: Some(target_name.clone().into()),
551 target: Some(target.clone()),
552 rights,
553 subdir: subdir.as_ref().map(|s| s.clone().into()),
554 availability: Some(availability),
555 ..Default::default()
556 }))
557 }
558 } else if let Some(n) = expose.runner() {
559 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
560 let source_names = n.into_iter();
561 let target_names = all_target_capability_names(expose, expose)
562 .ok_or_else(|| Error::internal("no capability"))?;
563 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
564 {
565 out_exposes.push(fdecl::Expose::Runner(fdecl::ExposeRunner {
566 source: Some(source.clone()),
567 source_name: Some(source_name.clone().into()),
568 #[cfg(fuchsia_api_level_at_least = "25")]
569 source_dictionary: source_dictionary.clone(),
570 target: Some(target.clone()),
571 target_name: Some(target_name.clone().into()),
572 ..Default::default()
573 }))
574 }
575 } else if let Some(n) = expose.resolver() {
576 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
577 let source_names = n.into_iter();
578 let target_names = all_target_capability_names(expose, expose)
579 .ok_or_else(|| Error::internal("no capability"))?;
580 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
581 {
582 out_exposes.push(fdecl::Expose::Resolver(fdecl::ExposeResolver {
583 source: Some(source.clone()),
584 source_name: Some(source_name.clone().into()),
585 #[cfg(fuchsia_api_level_at_least = "25")]
586 source_dictionary: source_dictionary.clone(),
587 target: Some(target.clone()),
588 target_name: Some(target_name.clone().into()),
589 ..Default::default()
590 }))
591 }
592 } else if let Some(n) = expose.dictionary() {
593 #[cfg(fuchsia_api_level_less_than = "25")]
594 {
595 return Err(Error::validate(format!(
596 "expose: dictionaries are not supported at this API level"
597 )));
598 }
599
600 #[cfg(fuchsia_api_level_at_least = "25")]
601 {
602 let (source, source_dictionary) =
603 extract_single_expose_source(options, expose, None)?;
604 let source_names = n.into_iter();
605 let target_names = all_target_capability_names(expose, expose)
606 .ok_or_else(|| Error::internal("no capability"))?;
607 for (source_name, target_name) in
608 source_names.into_iter().zip(target_names.into_iter())
609 {
610 let DerivedSourceInfo { source, source_dictionary, availability } =
611 derive_source_and_availability(
612 expose.availability.as_ref(),
613 source.clone(),
614 source_dictionary.clone(),
615 expose.source_availability.as_ref(),
616 all_capability_names,
617 all_children,
618 all_collections,
619 );
620 out_exposes.push(fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
621 source: Some(source),
622 source_name: Some(source_name.clone().into()),
623 source_dictionary,
624 target_name: Some(target_name.clone().into()),
625 target: Some(target.clone()),
626 availability: Some(availability),
627 ..Default::default()
628 }))
629 }
630 }
631 } else if let Some(n) = expose.config() {
632 #[cfg(fuchsia_api_level_less_than = "20")]
633 {
634 return Err(Error::validate(format!(
635 "expose: config blocks not supported at this API level"
636 )));
637 }
638 #[cfg(fuchsia_api_level_at_least = "20")]
639 {
640 let (source, source_dictionary) =
641 extract_single_expose_source(options, expose, None)?;
642 let source_names = n.into_iter();
643 let target_names = all_target_capability_names(expose, expose)
644 .ok_or_else(|| Error::internal("no capability"))?;
645 for (source_name, target_name) in
646 source_names.into_iter().zip(target_names.into_iter())
647 {
648 let DerivedSourceInfo { source, source_dictionary, availability } =
649 derive_source_and_availability(
650 expose.availability.as_ref(),
651 source.clone(),
652 source_dictionary.clone(),
653 expose.source_availability.as_ref(),
654 all_capability_names,
655 all_children,
656 all_collections,
657 );
658 out_exposes.push(fdecl::Expose::Config(fdecl::ExposeConfiguration {
659 source: Some(source.clone()),
660 source_name: Some(source_name.clone().into()),
661 #[cfg(fuchsia_api_level_at_least = "25")]
662 source_dictionary,
663 target: Some(target.clone()),
664 target_name: Some(target_name.clone().into()),
665 availability: Some(availability),
666 ..Default::default()
667 }))
668 }
669 }
670 } else {
671 return Err(Error::internal(format!("expose: must specify a known capability")));
672 }
673 }
674 Ok(out_exposes)
675}
676
677impl<T> Into<Vec<T>> for OneOrMany<T> {
678 fn into(self) -> Vec<T> {
679 match self {
680 OneOrMany::One(one) => vec![one],
681 OneOrMany::Many(many) => many,
682 }
683 }
684}
685
686fn annotate_type<T>(val: T) -> T {
688 val
689}
690
691struct DerivedSourceInfo {
692 source: fdecl::Ref,
693 source_dictionary: Option<String>,
694 availability: fdecl::Availability,
695}
696
697fn derive_source_and_availability(
700 availability: Option<&Availability>,
701 source: fdecl::Ref,
702 source_dictionary: Option<String>,
703 source_availability: Option<&SourceAvailability>,
704 all_capability_names: &BTreeSet<&Name>,
705 all_children: &BTreeSet<&Name>,
706 all_collections: &BTreeSet<&Name>,
707) -> DerivedSourceInfo {
708 let availability = availability.map(|a| match a {
709 Availability::Required => fdecl::Availability::Required,
710 Availability::Optional => fdecl::Availability::Optional,
711 Availability::SameAsTarget => fdecl::Availability::SameAsTarget,
712 Availability::Transitional => fdecl::Availability::Transitional,
713 });
714 if source_availability != Some(&SourceAvailability::Unknown) {
715 return DerivedSourceInfo {
716 source,
717 source_dictionary,
718 availability: availability.unwrap_or(fdecl::Availability::Required),
719 };
720 }
721 match &source {
722 fdecl::Ref::Child(fdecl::ChildRef { name, .. })
723 if !all_children.contains(&Name::new(name.clone()).unwrap()) =>
724 {
725 DerivedSourceInfo {
726 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
727 source_dictionary: None,
728 availability: availability.unwrap_or(fdecl::Availability::Optional),
729 }
730 }
731 fdecl::Ref::Collection(fdecl::CollectionRef { name, .. })
732 if !all_collections.contains(&Name::new(name.clone()).unwrap()) =>
733 {
734 DerivedSourceInfo {
735 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
736 source_dictionary: None,
737 availability: availability.unwrap_or(fdecl::Availability::Optional),
738 }
739 }
740 fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. })
741 if !all_capability_names.contains(&Name::new(name.clone()).unwrap()) =>
742 {
743 DerivedSourceInfo {
744 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
745 source_dictionary: None,
746 availability: availability.unwrap_or(fdecl::Availability::Optional),
747 }
748 }
749 _ => DerivedSourceInfo {
750 source,
751 source_dictionary,
752 availability: availability.unwrap_or(fdecl::Availability::Required),
753 },
754 }
755}
756
757fn maybe_generate_direct_offer_from_all(
760 offer_to_all: &Offer,
761 direct_offers: &[Offer],
762 target: &cm_types::Name,
763) -> Vec<Offer> {
764 assert!(offer_to_all.protocol.is_some() || offer_to_all.dictionary.is_some());
765 let mut returned_offers = vec![];
766 for mut local_offer in offer_to_all
767 .protocol
768 .as_ref()
769 .unwrap_or(&OneOrMany::Many(vec![]))
770 .iter()
771 .map(|individual_protocol| {
772 let mut local_offer = offer_to_all.clone();
773 local_offer.protocol = Some(OneOrMany::One(individual_protocol.clone()));
774 local_offer
775 })
776 .chain(
777 offer_to_all.dictionary.as_ref().unwrap_or(&OneOrMany::Many(vec![])).into_iter().map(
778 |dictionary| {
779 let mut local_offer = offer_to_all.clone();
780 local_offer.dictionary = Some(OneOrMany::One(dictionary.clone()));
781 local_offer
782 },
783 ),
784 )
785 {
786 let disallowed_offer_source = OfferFromRef::Named(target.clone());
787 if direct_offers.iter().all(|direct| {
788 !offer_to_all_would_duplicate(&local_offer, direct, target).unwrap()
791 }) && !local_offer.from.iter().any(|from| from == &disallowed_offer_source)
792 {
793 local_offer.to = OneOrMany::One(OfferToRef::Named((*target).clone()));
794 returned_offers.push(local_offer);
795 }
796 }
797
798 returned_offers
799}
800
801fn expand_offer_to_all(
802 offers_in: &Vec<Offer>,
803 children: &BTreeSet<&Name>,
804 collections: &BTreeSet<&Name>,
805) -> Result<Vec<Offer>, Error> {
806 let offers_to_all =
807 offers_in.iter().filter(|offer| matches!(offer.to, OneOrMany::One(OfferToRef::All)));
808
809 let mut direct_offers = offers_in
810 .iter()
811 .filter(|o| !matches!(o.to, OneOrMany::One(OfferToRef::All)))
812 .map(Offer::clone)
813 .collect::<Vec<Offer>>();
814
815 for offer_to_all in offers_to_all {
816 for target in children.iter().chain(collections.iter()) {
817 let offers = maybe_generate_direct_offer_from_all(offer_to_all, &direct_offers, target);
818 for offer in offers {
819 direct_offers.push(offer);
820 }
821 }
822 }
823
824 Ok(direct_offers)
825}
826
827fn translate_offer(
829 options: &CompileOptions<'_>,
830 offer_in: &Vec<Offer>,
831 all_capability_names: &BTreeSet<&Name>,
832 all_children: &BTreeSet<&Name>,
833 all_collections: &BTreeSet<&Name>,
834) -> Result<Vec<fdecl::Offer>, Error> {
835 let mut out_offers = vec![];
836 let expanded_offers = expand_offer_to_all(offer_in, all_children, all_collections)?;
837 for offer in &expanded_offers {
838 if let Some(n) = offer.service() {
839 let entries = extract_offer_sources_and_targets(
840 options,
841 offer,
842 n,
843 all_capability_names,
844 all_children,
845 all_collections,
846 )?;
847 for (source, source_dictionary, source_name, target, target_name) in entries {
848 let DerivedSourceInfo { source, source_dictionary, availability } =
849 derive_source_and_availability(
850 offer.availability.as_ref(),
851 source,
852 source_dictionary,
853 offer.source_availability.as_ref(),
854 all_capability_names,
855 all_children,
856 all_collections,
857 );
858 out_offers.push(fdecl::Offer::Service(fdecl::OfferService {
859 source: Some(source),
860 source_name: Some(source_name.into()),
861 #[cfg(fuchsia_api_level_at_least = "25")]
862 source_dictionary,
863 target: Some(target),
864 target_name: Some(target_name.into()),
865 availability: Some(availability),
866 #[cfg(fuchsia_api_level_at_least = "HEAD")]
867 dependency_type: Some(
868 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
869 ),
870 ..Default::default()
871 }));
872 }
873 } else if let Some(n) = offer.protocol() {
874 let entries = extract_offer_sources_and_targets(
875 options,
876 offer,
877 n,
878 all_capability_names,
879 all_children,
880 all_collections,
881 )?;
882 for (source, source_dictionary, source_name, target, target_name) in entries {
883 let DerivedSourceInfo { source, source_dictionary, availability } =
884 derive_source_and_availability(
885 offer.availability.as_ref(),
886 source,
887 source_dictionary,
888 offer.source_availability.as_ref(),
889 all_capability_names,
890 all_children,
891 all_collections,
892 );
893 out_offers.push(fdecl::Offer::Protocol(fdecl::OfferProtocol {
894 source: Some(source),
895 source_name: Some(source_name.into()),
896 #[cfg(fuchsia_api_level_at_least = "25")]
897 source_dictionary,
898 target: Some(target),
899 target_name: Some(target_name.into()),
900 dependency_type: Some(
901 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
902 ),
903 availability: Some(availability),
904 ..Default::default()
905 }));
906 }
907 } else if let Some(n) = offer.directory() {
908 let entries = extract_offer_sources_and_targets(
909 options,
910 offer,
911 n,
912 all_capability_names,
913 all_children,
914 all_collections,
915 )?;
916 for (source, source_dictionary, source_name, target, target_name) in entries {
917 let DerivedSourceInfo { source, source_dictionary, availability } =
918 derive_source_and_availability(
919 offer.availability.as_ref(),
920 source,
921 source_dictionary,
922 offer.source_availability.as_ref(),
923 all_capability_names,
924 all_children,
925 all_collections,
926 );
927 out_offers.push(fdecl::Offer::Directory(fdecl::OfferDirectory {
928 source: Some(source),
929 source_name: Some(source_name.into()),
930 #[cfg(fuchsia_api_level_at_least = "25")]
931 source_dictionary,
932 target: Some(target),
933 target_name: Some(target_name.into()),
934 rights: extract_offer_rights(offer)?,
935 subdir: extract_offer_subdir(offer).map(|s| s.into()),
936 dependency_type: Some(
937 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
938 ),
939 availability: Some(availability),
940 ..Default::default()
941 }));
942 }
943 } else if let Some(n) = offer.storage() {
944 let entries = extract_offer_sources_and_targets(
945 options,
946 offer,
947 n,
948 all_capability_names,
949 all_children,
950 all_collections,
951 )?;
952 for (source, source_dictionary, source_name, target, target_name) in entries {
953 let DerivedSourceInfo { source, source_dictionary: _, availability } =
954 derive_source_and_availability(
955 offer.availability.as_ref(),
956 source,
957 source_dictionary,
958 offer.source_availability.as_ref(),
959 all_capability_names,
960 all_children,
961 all_collections,
962 );
963 out_offers.push(fdecl::Offer::Storage(fdecl::OfferStorage {
964 source: Some(source),
965 source_name: Some(source_name.into()),
966 target: Some(target),
967 target_name: Some(target_name.into()),
968 availability: Some(availability),
969 ..Default::default()
970 }));
971 }
972 } else if let Some(n) = offer.runner() {
973 let entries = extract_offer_sources_and_targets(
974 options,
975 offer,
976 n,
977 all_capability_names,
978 all_children,
979 all_collections,
980 )?;
981 for (source, source_dictionary, source_name, target, target_name) in entries {
982 out_offers.push(fdecl::Offer::Runner(fdecl::OfferRunner {
983 source: Some(source),
984 source_name: Some(source_name.into()),
985 #[cfg(fuchsia_api_level_at_least = "25")]
986 source_dictionary,
987 target: Some(target),
988 target_name: Some(target_name.into()),
989 ..Default::default()
990 }));
991 }
992 } else if let Some(n) = offer.resolver() {
993 let entries = extract_offer_sources_and_targets(
994 options,
995 offer,
996 n,
997 all_capability_names,
998 all_children,
999 all_collections,
1000 )?;
1001 for (source, source_dictionary, source_name, target, target_name) in entries {
1002 out_offers.push(fdecl::Offer::Resolver(fdecl::OfferResolver {
1003 source: Some(source),
1004 source_name: Some(source_name.into()),
1005 #[cfg(fuchsia_api_level_at_least = "25")]
1006 source_dictionary,
1007 target: Some(target),
1008 target_name: Some(target_name.into()),
1009 ..Default::default()
1010 }));
1011 }
1012 } else if let Some(n) = offer.event_stream() {
1013 let entries = extract_offer_sources_and_targets(
1014 options,
1015 offer,
1016 n,
1017 all_capability_names,
1018 all_children,
1019 all_collections,
1020 )?;
1021 for (source, source_dictionary, source_name, target, target_name) in entries {
1022 let DerivedSourceInfo { source, source_dictionary: _, availability } =
1023 derive_source_and_availability(
1024 offer.availability.as_ref(),
1025 source,
1026 source_dictionary,
1027 offer.source_availability.as_ref(),
1028 all_capability_names,
1029 all_children,
1030 all_collections,
1031 );
1032 let scopes = match offer.scope.clone() {
1033 Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
1034 None => None,
1035 };
1036 out_offers.push(fdecl::Offer::EventStream(fdecl::OfferEventStream {
1037 source: Some(source),
1038 source_name: Some(source_name.into()),
1039 target: Some(target),
1040 target_name: Some(target_name.into()),
1041 scope: match scopes {
1042 Some(values) => {
1043 let mut output = vec![];
1044 for value in &values {
1045 static EMPTY_SET: BTreeSet<&Name> = BTreeSet::new();
1046 output.push(translate_target_ref(
1047 options,
1048 value.into(),
1049 &all_children,
1050 &all_collections,
1051 &EMPTY_SET,
1052 )?);
1053 }
1054 Some(output)
1055 }
1056 None => None,
1057 },
1058 availability: Some(availability),
1059 ..Default::default()
1060 }));
1061 }
1062 } else if let Some(n) = offer.dictionary() {
1063 #[cfg(fuchsia_api_level_less_than = "25")]
1064 {
1065 return Err(Error::validate(format!(
1066 "offer: dictionaries are not supported at this API level"
1067 )));
1068 }
1069 #[cfg(fuchsia_api_level_at_least = "25")]
1070 {
1071 let entries = extract_offer_sources_and_targets(
1072 options,
1073 offer,
1074 n,
1075 all_capability_names,
1076 all_children,
1077 all_collections,
1078 )?;
1079 for (source, source_dictionary, source_name, target, target_name) in entries {
1080 let DerivedSourceInfo { source, source_dictionary, availability } =
1081 derive_source_and_availability(
1082 offer.availability.as_ref(),
1083 source,
1084 source_dictionary,
1085 offer.source_availability.as_ref(),
1086 all_capability_names,
1087 all_children,
1088 all_collections,
1089 );
1090 out_offers.push(fdecl::Offer::Dictionary(fdecl::OfferDictionary {
1091 source: Some(source),
1092 source_name: Some(source_name.into()),
1093 source_dictionary,
1094 target: Some(target),
1095 target_name: Some(target_name.into()),
1096 dependency_type: Some(
1097 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1098 ),
1099 availability: Some(availability),
1100 ..Default::default()
1101 }));
1102 }
1103 }
1104 } else if let Some(n) = offer.config() {
1105 #[cfg(fuchsia_api_level_less_than = "20")]
1106 {
1107 return Err(Error::validate(format!(
1108 "offer: config blocks not supported at this API level"
1109 )));
1110 }
1111 #[cfg(fuchsia_api_level_at_least = "20")]
1112 {
1113 let entries = extract_offer_sources_and_targets(
1114 options,
1115 offer,
1116 n,
1117 all_capability_names,
1118 all_children,
1119 all_collections,
1120 )?;
1121 for (source, source_dictionary, source_name, target, target_name) in entries {
1122 let DerivedSourceInfo { source, source_dictionary, availability } =
1123 derive_source_and_availability(
1124 offer.availability.as_ref(),
1125 source,
1126 source_dictionary,
1127 offer.source_availability.as_ref(),
1128 all_capability_names,
1129 all_children,
1130 all_collections,
1131 );
1132 out_offers.push(fdecl::Offer::Config(fdecl::OfferConfiguration {
1133 source: Some(source),
1134 source_name: Some(source_name.into()),
1135 target: Some(target),
1136 target_name: Some(target_name.into()),
1137 availability: Some(availability),
1138 #[cfg(fuchsia_api_level_at_least = "25")]
1139 source_dictionary,
1140 ..Default::default()
1141 }));
1142 }
1143 }
1144 } else {
1145 return Err(Error::internal(format!("no capability")));
1146 }
1147 }
1148 Ok(out_offers)
1149}
1150
1151fn translate_children(children_in: &Vec<Child>) -> Result<Vec<fdecl::Child>, Error> {
1152 let mut out_children = vec![];
1153 for child in children_in.iter() {
1154 out_children.push(fdecl::Child {
1155 name: Some(child.name.clone().into()),
1156 url: Some(child.url.clone().into()),
1157 startup: Some(child.startup.clone().into()),
1158 environment: extract_environment_ref(child.environment.as_ref()).map(|e| e.into()),
1159 on_terminate: child.on_terminate.as_ref().map(|r| r.clone().into()),
1160 ..Default::default()
1161 });
1162 }
1163 Ok(out_children)
1164}
1165
1166fn translate_collections(
1167 collections_in: &Vec<Collection>,
1168) -> Result<Vec<fdecl::Collection>, Error> {
1169 let mut out_collections = vec![];
1170 for collection in collections_in.iter() {
1171 out_collections.push(fdecl::Collection {
1172 name: Some(collection.name.clone().into()),
1173 durability: Some(collection.durability.clone().into()),
1174 environment: extract_environment_ref(collection.environment.as_ref()).map(|e| e.into()),
1175 allowed_offers: collection.allowed_offers.clone().map(|a| a.into()),
1176 allow_long_names: collection.allow_long_names.clone(),
1177 persistent_storage: collection.persistent_storage.clone(),
1178 ..Default::default()
1179 });
1180 }
1181 Ok(out_collections)
1182}
1183
1184fn translate_nested_value_type(nested_type: &ConfigNestedValueType) -> fdecl::ConfigType {
1186 let layout = match nested_type {
1187 ConfigNestedValueType::Bool {} => fdecl::ConfigTypeLayout::Bool,
1188 ConfigNestedValueType::Uint8 {} => fdecl::ConfigTypeLayout::Uint8,
1189 ConfigNestedValueType::Uint16 {} => fdecl::ConfigTypeLayout::Uint16,
1190 ConfigNestedValueType::Uint32 {} => fdecl::ConfigTypeLayout::Uint32,
1191 ConfigNestedValueType::Uint64 {} => fdecl::ConfigTypeLayout::Uint64,
1192 ConfigNestedValueType::Int8 {} => fdecl::ConfigTypeLayout::Int8,
1193 ConfigNestedValueType::Int16 {} => fdecl::ConfigTypeLayout::Int16,
1194 ConfigNestedValueType::Int32 {} => fdecl::ConfigTypeLayout::Int32,
1195 ConfigNestedValueType::Int64 {} => fdecl::ConfigTypeLayout::Int64,
1196 ConfigNestedValueType::String { .. } => fdecl::ConfigTypeLayout::String,
1197 };
1198 let constraints = match nested_type {
1199 ConfigNestedValueType::String { max_size } => {
1200 vec![fdecl::LayoutConstraint::MaxSize(max_size.get())]
1201 }
1202 _ => vec![],
1203 };
1204 fdecl::ConfigType {
1205 layout,
1206 constraints,
1207 parameters: Some(vec![]),
1211 }
1212}
1213
1214fn translate_value_type(
1216 value_type: &ConfigValueType,
1217) -> (fdecl::ConfigType, fdecl::ConfigMutability) {
1218 let (layout, source_mutability) = match value_type {
1219 ConfigValueType::Bool { mutability } => (fdecl::ConfigTypeLayout::Bool, mutability),
1220 ConfigValueType::Uint8 { mutability } => (fdecl::ConfigTypeLayout::Uint8, mutability),
1221 ConfigValueType::Uint16 { mutability } => (fdecl::ConfigTypeLayout::Uint16, mutability),
1222 ConfigValueType::Uint32 { mutability } => (fdecl::ConfigTypeLayout::Uint32, mutability),
1223 ConfigValueType::Uint64 { mutability } => (fdecl::ConfigTypeLayout::Uint64, mutability),
1224 ConfigValueType::Int8 { mutability } => (fdecl::ConfigTypeLayout::Int8, mutability),
1225 ConfigValueType::Int16 { mutability } => (fdecl::ConfigTypeLayout::Int16, mutability),
1226 ConfigValueType::Int32 { mutability } => (fdecl::ConfigTypeLayout::Int32, mutability),
1227 ConfigValueType::Int64 { mutability } => (fdecl::ConfigTypeLayout::Int64, mutability),
1228 ConfigValueType::String { mutability, .. } => (fdecl::ConfigTypeLayout::String, mutability),
1229 ConfigValueType::Vector { mutability, .. } => (fdecl::ConfigTypeLayout::Vector, mutability),
1230 };
1231 let (constraints, parameters) = match value_type {
1232 ConfigValueType::String { max_size, .. } => {
1233 (vec![fdecl::LayoutConstraint::MaxSize(max_size.get())], vec![])
1234 }
1235 ConfigValueType::Vector { max_count, element, .. } => {
1236 let nested_type = translate_nested_value_type(element);
1237 (
1238 vec![fdecl::LayoutConstraint::MaxSize(max_count.get())],
1239 vec![fdecl::LayoutParameter::NestedType(nested_type)],
1240 )
1241 }
1242 _ => (vec![], vec![]),
1243 };
1244 let mut mutability = fdecl::ConfigMutability::empty();
1245 if let Some(source_mutability) = source_mutability {
1246 for source in source_mutability {
1247 match source {
1248 ConfigRuntimeSource::Parent => mutability |= fdecl::ConfigMutability::PARENT,
1249 }
1250 }
1251 }
1252 (
1253 fdecl::ConfigType {
1254 layout,
1255 constraints,
1256 parameters: Some(parameters),
1260 },
1261 mutability,
1262 )
1263}
1264
1265fn translate_config(
1268 fields: &Option<BTreeMap<ConfigKey, ConfigValueType>>,
1269 uses: &Option<Vec<Use>>,
1270 package_path: &Option<String>,
1271) -> Result<Option<fdecl::ConfigSchema>, Error> {
1272 let mut use_fields: BTreeMap<ConfigKey, ConfigValueType> = uses
1273 .iter()
1274 .flatten()
1275 .map(|u| {
1276 if u.config.is_none() {
1277 return None;
1278 }
1279 let key = ConfigKey(u.key.clone().expect("key should be set").into());
1280 let config_type =
1281 validate::use_config_to_value_type(u).expect("config type should be valid");
1282 Some((key, config_type))
1283 })
1284 .flatten()
1285 .collect();
1286 for (key, value) in fields.iter().flatten() {
1287 if use_fields.contains_key(key) {
1288 if use_fields.get(key) != Some(&value) {
1289 return Err(Error::validate(format!(
1290 "Config error: `use` and `config` block contain key '{}' with different types",
1291 key
1292 )));
1293 }
1294 }
1295 use_fields.insert(key.clone(), value.clone());
1296 }
1297
1298 if use_fields.is_empty() {
1299 return Ok(None);
1300 }
1301
1302 let source = match fields.as_ref().map_or(true, |f| f.is_empty()) {
1303 #[cfg(fuchsia_api_level_at_least = "20")]
1305 true => fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default()),
1306 _ => {
1308 let Some(package_path) = package_path.as_ref() else {
1309 return Err(Error::invalid_args(
1310 "can't translate config: no package path for value file",
1311 ));
1312 };
1313 fdecl::ConfigValueSource::PackagePath(package_path.to_owned())
1314 }
1315 };
1316
1317 let mut fidl_fields = vec![];
1318
1319 let mut hasher = Sha256::new();
1321
1322 for (key, value) in &use_fields {
1323 let (type_, mutability) = translate_value_type(value);
1324
1325 fidl_fields.push(fdecl::ConfigField {
1326 key: Some(key.to_string()),
1327 type_: Some(type_),
1328 mutability: Some(mutability),
1329 ..Default::default()
1330 });
1331
1332 hasher.update(key.as_str());
1333
1334 value.update_digest(&mut hasher);
1335 }
1336
1337 let hash = hasher.finalize();
1338 let checksum = fdecl::ConfigChecksum::Sha256(*hash.as_ref());
1339
1340 Ok(Some(fdecl::ConfigSchema {
1341 fields: Some(fidl_fields),
1342 checksum: Some(checksum),
1343 value_source: Some(source),
1344 ..Default::default()
1345 }))
1346}
1347
1348fn translate_environments(
1349 options: &CompileOptions<'_>,
1350 envs_in: &Vec<Environment>,
1351 all_capability_names: &BTreeSet<&Name>,
1352) -> Result<Vec<fdecl::Environment>, Error> {
1353 envs_in
1354 .iter()
1355 .map(|env| {
1356 Ok(fdecl::Environment {
1357 name: Some(env.name.clone().into()),
1358 extends: match env.extends {
1359 Some(EnvironmentExtends::Realm) => Some(fdecl::EnvironmentExtends::Realm),
1360 Some(EnvironmentExtends::None) => Some(fdecl::EnvironmentExtends::None),
1361 None => Some(fdecl::EnvironmentExtends::None),
1362 },
1363 runners: env
1364 .runners
1365 .as_ref()
1366 .map(|runners| {
1367 runners
1368 .iter()
1369 .map(|r| translate_runner_registration(options, r))
1370 .collect::<Result<Vec<_>, Error>>()
1371 })
1372 .transpose()?,
1373 resolvers: env
1374 .resolvers
1375 .as_ref()
1376 .map(|resolvers| {
1377 resolvers
1378 .iter()
1379 .map(|r| translate_resolver_registration(options, r))
1380 .collect::<Result<Vec<_>, Error>>()
1381 })
1382 .transpose()?,
1383 debug_capabilities: env
1384 .debug
1385 .as_ref()
1386 .map(|debug_capabiltities| {
1387 translate_debug_capabilities(
1388 options,
1389 debug_capabiltities,
1390 all_capability_names,
1391 )
1392 })
1393 .transpose()?,
1394 stop_timeout_ms: env.stop_timeout_ms.map(|s| s.0),
1395 ..Default::default()
1396 })
1397 })
1398 .collect()
1399}
1400
1401fn translate_runner_registration(
1402 options: &CompileOptions<'_>,
1403 reg: &RunnerRegistration,
1404) -> Result<fdecl::RunnerRegistration, Error> {
1405 let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
1406 Ok(fdecl::RunnerRegistration {
1407 source_name: Some(reg.runner.clone().into()),
1408 source: Some(source),
1409 target_name: Some(reg.r#as.as_ref().unwrap_or(®.runner).clone().into()),
1410 ..Default::default()
1411 })
1412}
1413
1414fn translate_resolver_registration(
1415 options: &CompileOptions<'_>,
1416 reg: &ResolverRegistration,
1417) -> Result<fdecl::ResolverRegistration, Error> {
1418 let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
1419 Ok(fdecl::ResolverRegistration {
1420 resolver: Some(reg.resolver.clone().into()),
1421 source: Some(source),
1422 scheme: Some(
1423 reg.scheme
1424 .as_str()
1425 .parse::<cm_types::UrlScheme>()
1426 .map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))?
1427 .into(),
1428 ),
1429 ..Default::default()
1430 })
1431}
1432
1433fn translate_debug_capabilities(
1434 options: &CompileOptions<'_>,
1435 capabilities: &Vec<DebugRegistration>,
1436 all_capability_names: &BTreeSet<&Name>,
1437) -> Result<Vec<fdecl::DebugRegistration>, Error> {
1438 let mut out_capabilities = vec![];
1439 for capability in capabilities {
1440 if let Some(n) = capability.protocol() {
1441 let (source, _source_dictionary) =
1442 extract_single_offer_source(options, capability, Some(all_capability_names))?;
1443 let targets = all_target_capability_names(capability, capability)
1444 .ok_or_else(|| Error::internal("no capability"))?;
1445 let source_names = n;
1446 for target_name in targets {
1447 let source_name = if source_names.len() == 1 {
1456 (*source_names.iter().next().unwrap()).clone()
1457 } else {
1458 target_name.clone()
1459 };
1460 out_capabilities.push(fdecl::DebugRegistration::Protocol(
1461 fdecl::DebugProtocolRegistration {
1462 source: Some(source.clone()),
1463 source_name: Some(source_name.into()),
1464 target_name: Some(target_name.clone().into()),
1465 ..Default::default()
1466 },
1467 ));
1468 }
1469 }
1470 }
1471 Ok(out_capabilities)
1472}
1473
1474fn extract_use_source(
1475 options: &CompileOptions<'_>,
1476 in_obj: &Use,
1477 all_capability_names: &BTreeSet<&Name>,
1478 all_children_names: &BTreeSet<&Name>,
1479 all_collection_names: Option<&BTreeSet<&Name>>,
1480) -> Result<(fdecl::Ref, Option<String>), Error> {
1481 let ref_ = match in_obj.from.as_ref() {
1482 Some(UseFromRef::Parent) => fdecl::Ref::Parent(fdecl::ParentRef {}),
1483 Some(UseFromRef::Framework) => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
1484 Some(UseFromRef::Debug) => fdecl::Ref::Debug(fdecl::DebugRef {}),
1485 Some(UseFromRef::Self_) => fdecl::Ref::Self_(fdecl::SelfRef {}),
1486 Some(UseFromRef::Named(name)) => {
1487 if all_capability_names.contains(&name) {
1488 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
1489 } else if all_children_names.contains(&name) {
1490 fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
1491 } else if all_collection_names.is_some()
1492 && all_collection_names.unwrap().contains(&name)
1493 {
1494 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() })
1495 } else {
1496 return Err(Error::internal(format!(
1497 "use source \"{:?}\" not supported for \"use from\"",
1498 name
1499 )));
1500 }
1501 }
1502 Some(UseFromRef::Dictionary(d)) => {
1503 return dictionary_ref_to_source(&d);
1504 }
1505 None => fdecl::Ref::Parent(fdecl::ParentRef {}), };
1507 Ok((ref_, None))
1508}
1509
1510fn extract_use_availability(in_obj: &Use) -> Result<fdecl::Availability, Error> {
1511 match in_obj.availability.as_ref() {
1512 Some(Availability::Required) | None => Ok(fdecl::Availability::Required),
1513 Some(Availability::Optional) => Ok(fdecl::Availability::Optional),
1514 Some(Availability::Transitional) => Ok(fdecl::Availability::Transitional),
1515 Some(Availability::SameAsTarget) => Err(Error::internal(
1516 "availability \"same_as_target\" not supported for use declarations",
1517 )),
1518 }
1519}
1520
1521fn extract_use_subdir(in_obj: &Use) -> Option<cm::RelativePath> {
1522 in_obj.subdir.clone()
1523}
1524
1525fn extract_expose_subdir(in_obj: &Expose) -> Option<cm::RelativePath> {
1526 in_obj.subdir.clone()
1527}
1528
1529fn extract_offer_subdir(in_obj: &Offer) -> Option<cm::RelativePath> {
1530 in_obj.subdir.clone()
1531}
1532
1533fn extract_expose_rights(in_obj: &Expose) -> Result<Option<fio::Operations>, Error> {
1534 match in_obj.rights.as_ref() {
1535 Some(rights_tokens) => {
1536 let mut rights = Vec::new();
1537 for token in rights_tokens.0.iter() {
1538 rights.append(&mut token.expand())
1539 }
1540 if rights.is_empty() {
1541 return Err(Error::missing_rights(
1542 "Rights provided to expose are not well formed.",
1543 ));
1544 }
1545 let mut seen_rights = BTreeSet::new();
1546 let mut operations: fio::Operations = fio::Operations::empty();
1547 for right in rights.iter() {
1548 if seen_rights.contains(&right) {
1549 return Err(Error::duplicate_rights(
1550 "Rights provided to expose are not well formed.",
1551 ));
1552 }
1553 seen_rights.insert(right);
1554 operations |= *right;
1555 }
1556
1557 Ok(Some(operations))
1558 }
1559 None => Ok(None),
1561 }
1562}
1563
1564fn expose_source_from_ref(
1565 options: &CompileOptions<'_>,
1566 reference: &ExposeFromRef,
1567 all_capability_names: Option<&BTreeSet<&Name>>,
1568 all_collections: Option<&BTreeSet<&Name>>,
1569) -> Result<(fdecl::Ref, Option<String>), Error> {
1570 let ref_ = match reference {
1571 ExposeFromRef::Named(name) => {
1572 if all_capability_names.is_some() && all_capability_names.unwrap().contains(&name) {
1573 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
1574 } else if all_collections.is_some() && all_collections.unwrap().contains(&name) {
1575 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() })
1576 } else {
1577 fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
1578 }
1579 }
1580 ExposeFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
1581 ExposeFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
1582 ExposeFromRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
1583 ExposeFromRef::Dictionary(d) => {
1584 return dictionary_ref_to_source(&d);
1585 }
1586 };
1587 Ok((ref_, None))
1588}
1589
1590fn extract_single_expose_source(
1591 options: &CompileOptions<'_>,
1592 in_obj: &Expose,
1593 all_capability_names: Option<&BTreeSet<&Name>>,
1594) -> Result<(fdecl::Ref, Option<String>), Error> {
1595 match &in_obj.from {
1596 OneOrMany::One(reference) => {
1597 expose_source_from_ref(options, &reference, all_capability_names, None)
1598 }
1599 OneOrMany::Many(many) => {
1600 return Err(Error::internal(format!(
1601 "multiple unexpected \"from\" clauses for \"expose\": {:?}",
1602 many
1603 )))
1604 }
1605 }
1606}
1607
1608fn extract_all_expose_sources(
1609 options: &CompileOptions<'_>,
1610 in_obj: &Expose,
1611 all_collections: Option<&BTreeSet<&Name>>,
1612) -> Result<Vec<(fdecl::Ref, Option<String>)>, Error> {
1613 in_obj.from.iter().map(|e| expose_source_from_ref(options, e, None, all_collections)).collect()
1614}
1615
1616fn extract_offer_rights(in_obj: &Offer) -> Result<Option<fio::Operations>, Error> {
1617 match in_obj.rights.as_ref() {
1618 Some(rights_tokens) => {
1619 let mut rights = Vec::new();
1620 for token in rights_tokens.0.iter() {
1621 rights.append(&mut token.expand())
1622 }
1623 if rights.is_empty() {
1624 return Err(Error::missing_rights("Rights provided to offer are not well formed."));
1625 }
1626 let mut seen_rights = BTreeSet::new();
1627 let mut operations: fio::Operations = fio::Operations::empty();
1628 for right in rights.iter() {
1629 if seen_rights.contains(&right) {
1630 return Err(Error::duplicate_rights(
1631 "Rights provided to offer are not well formed.",
1632 ));
1633 }
1634 seen_rights.insert(right);
1635 operations |= *right;
1636 }
1637
1638 Ok(Some(operations))
1639 }
1640 None => Ok(None),
1642 }
1643}
1644
1645fn extract_single_offer_source<T>(
1646 options: &CompileOptions<'_>,
1647 in_obj: &T,
1648 all_capability_names: Option<&BTreeSet<&Name>>,
1649) -> Result<(fdecl::Ref, Option<String>), Error>
1650where
1651 T: FromClause,
1652{
1653 match in_obj.from_() {
1654 OneOrMany::One(reference) => {
1655 any_ref_to_decl(options, reference, all_capability_names, None)
1656 }
1657 many => {
1658 return Err(Error::internal(format!(
1659 "multiple unexpected \"from\" clauses for \"offer\": {}",
1660 many
1661 )))
1662 }
1663 }
1664}
1665
1666fn extract_all_offer_sources<T: FromClause>(
1667 options: &CompileOptions<'_>,
1668 in_obj: &T,
1669 all_capability_names: &BTreeSet<&Name>,
1670 all_collections: &BTreeSet<&Name>,
1671) -> Result<Vec<(fdecl::Ref, Option<String>)>, Error> {
1672 in_obj
1673 .from_()
1674 .into_iter()
1675 .map(|r| {
1676 any_ref_to_decl(options, r.clone(), Some(all_capability_names), Some(all_collections))
1677 })
1678 .collect()
1679}
1680
1681fn translate_target_ref(
1682 options: &CompileOptions<'_>,
1683 reference: AnyRef<'_>,
1684 all_children: &BTreeSet<&Name>,
1685 all_collections: &BTreeSet<&Name>,
1686 all_capabilities: &BTreeSet<&Name>,
1687) -> Result<fdecl::Ref, Error> {
1688 match reference {
1689 AnyRef::Named(name) if all_children.contains(name) => {
1690 Ok(fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None }))
1691 }
1692 AnyRef::Named(name) if all_collections.contains(name) => {
1693 Ok(fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() }))
1694 }
1695 AnyRef::Named(name) if all_capabilities.contains(name) => {
1696 Ok(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() }))
1697 }
1698 AnyRef::OwnDictionary(name) if all_capabilities.contains(name) => {
1699 #[cfg(fuchsia_api_level_at_least = "25")]
1700 return Ok(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() }));
1701 #[cfg(fuchsia_api_level_less_than = "25")]
1702 return Err(Error::validate("dictionaries are not supported at this API level"));
1703 }
1704 AnyRef::Named(_) => Err(Error::internal(format!("dangling reference: \"{}\"", reference))),
1705 _ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))),
1706 }
1707}
1708
1709fn extract_offer_sources_and_targets(
1712 options: &CompileOptions<'_>,
1713 offer: &Offer,
1714 source_names: OneOrMany<&Name>,
1715 all_capability_names: &BTreeSet<&Name>,
1716 all_children: &BTreeSet<&Name>,
1717 all_collections: &BTreeSet<&Name>,
1718) -> Result<Vec<(fdecl::Ref, Option<String>, Name, fdecl::Ref, Name)>, Error> {
1719 let mut out = vec![];
1720
1721 let sources = extract_all_offer_sources(options, offer, all_capability_names, all_collections)?;
1722 let target_names = all_target_capability_names(offer, offer)
1723 .ok_or_else(|| Error::internal("no capability".to_string()))?;
1724
1725 for (source, source_dictionary) in sources {
1726 for to in &offer.to {
1727 for target_name in &target_names {
1728 let source_name = if source_names.len() == 1 {
1733 (*source_names.iter().next().unwrap()).clone()
1734 } else {
1735 (*target_name).clone()
1736 };
1737 let target = translate_target_ref(
1738 options,
1739 to.into(),
1740 all_children,
1741 all_collections,
1742 all_capability_names,
1743 )?;
1744 out.push((
1745 source.clone(),
1746 source_dictionary.clone(),
1747 source_name,
1748 target,
1749 (*target_name).clone(),
1750 ))
1751 }
1752 }
1753 }
1754 Ok(out)
1755}
1756
1757fn all_target_use_paths<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<Path>>
1759where
1760 T: CapabilityClause,
1761 U: PathClause,
1762{
1763 if let Some(n) = in_obj.service() {
1764 Some(svc_paths_from_names(n, to_obj))
1765 } else if let Some(n) = in_obj.protocol() {
1766 Some(svc_paths_from_names(n, to_obj))
1767 } else if let Some(_) = in_obj.directory() {
1768 let path = to_obj.path().expect("no path on use directory");
1769 Some(OneOrMany::One(path.clone()))
1770 } else if let Some(_) = in_obj.storage() {
1771 let path = to_obj.path().expect("no path on use storage");
1772 Some(OneOrMany::One(path.clone()))
1773 } else if let Some(_) = in_obj.event_stream() {
1774 let default_path = Path::new("/svc/fuchsia.component.EventStream").unwrap();
1775 let path = to_obj.path().unwrap_or(&default_path);
1776 Some(OneOrMany::One(path.clone()))
1777 } else {
1778 None
1779 }
1780}
1781
1782fn svc_paths_from_names<T>(names: OneOrMany<&Name>, to_obj: &T) -> OneOrMany<Path>
1785where
1786 T: PathClause,
1787{
1788 match names {
1789 OneOrMany::One(n) => {
1790 if let Some(path) = to_obj.path() {
1791 OneOrMany::One(path.clone())
1792 } else {
1793 OneOrMany::One(format!("/svc/{}", n).parse().unwrap())
1794 }
1795 }
1796 OneOrMany::Many(v) => {
1797 let many = v.iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect();
1798 OneOrMany::Many(many)
1799 }
1800 }
1801}
1802
1803fn one_target_use_path<T, U>(in_obj: &T, to_obj: &U) -> Result<Path, Error>
1805where
1806 T: CapabilityClause,
1807 U: PathClause,
1808{
1809 match all_target_use_paths(in_obj, to_obj) {
1810 Some(OneOrMany::One(target_name)) => Ok(target_name),
1811 Some(OneOrMany::Many(_)) => {
1812 Err(Error::internal("expecting one capability, but multiple provided"))
1813 }
1814 _ => Err(Error::internal("expecting one capability, but none provided")),
1815 }
1816}
1817
1818fn all_target_capability_names<'a, T, U>(
1820 in_obj: &'a T,
1821 to_obj: &'a U,
1822) -> Option<OneOrMany<&'a Name>>
1823where
1824 T: CapabilityClause,
1825 U: AsClause + PathClause,
1826{
1827 if let Some(as_) = to_obj.r#as() {
1828 Some(OneOrMany::One(as_))
1830 } else {
1831 if let Some(n) = in_obj.service() {
1832 Some(n)
1833 } else if let Some(n) = in_obj.protocol() {
1834 Some(n)
1835 } else if let Some(n) = in_obj.directory() {
1836 Some(n)
1837 } else if let Some(n) = in_obj.storage() {
1838 Some(n)
1839 } else if let Some(n) = in_obj.runner() {
1840 Some(n)
1841 } else if let Some(n) = in_obj.resolver() {
1842 Some(n)
1843 } else if let Some(n) = in_obj.event_stream() {
1844 Some(n)
1845 } else if let Some(n) = in_obj.dictionary() {
1846 Some(n)
1847 } else if let Some(n) = in_obj.config() {
1848 Some(n)
1849 } else {
1850 None
1851 }
1852 }
1853}
1854
1855fn extract_expose_target(in_obj: &Expose) -> Result<fdecl::Ref, Error> {
1856 match &in_obj.to {
1857 Some(ExposeToRef::Parent) => Ok(fdecl::Ref::Parent(fdecl::ParentRef {})),
1858 Some(ExposeToRef::Framework) => Ok(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
1859 None => Ok(fdecl::Ref::Parent(fdecl::ParentRef {})),
1860 }
1861}
1862
1863fn extract_environment_ref(r: Option<&EnvironmentRef>) -> Option<cm::Name> {
1864 r.map(|r| {
1865 let EnvironmentRef::Named(name) = r;
1866 name.clone()
1867 })
1868}
1869
1870pub fn translate_capabilities(
1871 options: &CompileOptions<'_>,
1872 capabilities_in: &Vec<Capability>,
1873 as_builtin: bool,
1874) -> Result<Vec<fdecl::Capability>, Error> {
1875 let mut out_capabilities = vec![];
1876 for capability in capabilities_in {
1877 if let Some(service) = &capability.service {
1878 for n in service.iter() {
1879 let source_path = match as_builtin {
1880 true => None,
1881 false => Some(
1882 capability
1883 .path
1884 .clone()
1885 .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
1886 .into(),
1887 ),
1888 };
1889 out_capabilities.push(fdecl::Capability::Service(fdecl::Service {
1890 name: Some(n.clone().into()),
1891 source_path,
1892 ..Default::default()
1893 }));
1894 }
1895 } else if let Some(protocol) = &capability.protocol {
1896 for n in protocol.iter() {
1897 let source_path = match as_builtin {
1898 true => None,
1899 false => Some(
1900 capability
1901 .path
1902 .clone()
1903 .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
1904 .into(),
1905 ),
1906 };
1907 out_capabilities.push(fdecl::Capability::Protocol(fdecl::Protocol {
1908 name: Some(n.clone().into()),
1909 source_path,
1910 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1911 delivery: capability.delivery.map(Into::into),
1912 ..Default::default()
1913 }));
1914 }
1915 } else if let Some(n) = &capability.directory {
1916 let source_path = match as_builtin {
1917 true => None,
1918 false => {
1919 Some(capability.path.as_ref().expect("missing source path").clone().into())
1920 }
1921 };
1922 let rights = extract_required_rights(capability, "capability")?;
1923 out_capabilities.push(fdecl::Capability::Directory(fdecl::Directory {
1924 name: Some(n.clone().into()),
1925 source_path,
1926 rights: Some(rights),
1927 ..Default::default()
1928 }));
1929 } else if let Some(n) = &capability.storage {
1930 if as_builtin {
1931 return Err(Error::internal(format!(
1932 "built-in storage capabilities are not supported"
1933 )));
1934 }
1935 let backing_dir = capability
1936 .backing_dir
1937 .as_ref()
1938 .expect("storage has no path or backing_dir")
1939 .clone()
1940 .into();
1941
1942 let (source, _source_dictionary) =
1943 any_ref_to_decl(options, capability.from.as_ref().unwrap().into(), None, None)?;
1944 out_capabilities.push(fdecl::Capability::Storage(fdecl::Storage {
1945 name: Some(n.clone().into()),
1946 backing_dir: Some(backing_dir),
1947 subdir: capability.subdir.clone().map(Into::into),
1948 source: Some(source),
1949 storage_id: Some(
1950 capability.storage_id.clone().expect("storage is missing storage_id").into(),
1951 ),
1952 ..Default::default()
1953 }));
1954 } else if let Some(n) = &capability.runner {
1955 let source_path = match as_builtin {
1956 true => None,
1957 false => {
1958 Some(capability.path.as_ref().expect("missing source path").clone().into())
1959 }
1960 };
1961 out_capabilities.push(fdecl::Capability::Runner(fdecl::Runner {
1962 name: Some(n.clone().into()),
1963 source_path,
1964 ..Default::default()
1965 }));
1966 } else if let Some(n) = &capability.resolver {
1967 let source_path = match as_builtin {
1968 true => None,
1969 false => {
1970 Some(capability.path.as_ref().expect("missing source path").clone().into())
1971 }
1972 };
1973 out_capabilities.push(fdecl::Capability::Resolver(fdecl::Resolver {
1974 name: Some(n.clone().into()),
1975 source_path,
1976 ..Default::default()
1977 }));
1978 } else if let Some(ns) = &capability.event_stream {
1979 if !as_builtin {
1980 return Err(Error::internal(format!(
1981 "event_stream capabilities may only be declared as built-in capabilities"
1982 )));
1983 }
1984 for n in ns {
1985 out_capabilities.push(fdecl::Capability::EventStream(fdecl::EventStream {
1986 name: Some(n.clone().into()),
1987 ..Default::default()
1988 }));
1989 }
1990 } else if let Some(n) = &capability.dictionary {
1991 #[cfg(fuchsia_api_level_less_than = "25")]
1992 {
1993 return Err(Error::validate(format!(
1994 "dictionary capabilities are not supported at this API level"
1995 )));
1996 }
1997 #[cfg(fuchsia_api_level_at_least = "25")]
1998 {
1999 out_capabilities.push(fdecl::Capability::Dictionary(fdecl::Dictionary {
2000 name: Some(n.clone().into()),
2001 source_path: capability.path.clone().map(Into::into),
2002 ..Default::default()
2003 }));
2004 }
2005 } else if let Some(c) = &capability.config {
2006 #[cfg(fuchsia_api_level_less_than = "20")]
2007 {
2008 return Err(Error::validate(format!(
2009 "configuration capabilities are not supported at this API level"
2010 )));
2011 }
2012 #[cfg(fuchsia_api_level_at_least = "20")]
2013 {
2014 let value = configuration_to_value(
2015 c,
2016 &capability,
2017 &capability.config_type,
2018 &capability.value,
2019 )?;
2020 out_capabilities.push(fdecl::Capability::Config(fdecl::Configuration {
2021 name: Some(c.clone().into()),
2022 value: Some(value),
2023 ..Default::default()
2024 }));
2025 }
2026 } else {
2027 return Err(Error::internal(format!("no capability declaration recognized")));
2028 }
2029 }
2030 Ok(out_capabilities)
2031}
2032
2033pub fn extract_required_rights<T>(in_obj: &T, keyword: &str) -> Result<fio::Operations, Error>
2034where
2035 T: RightsClause,
2036{
2037 match in_obj.rights() {
2038 Some(rights_tokens) => {
2039 let mut rights = Vec::new();
2040 for token in rights_tokens.0.iter() {
2041 rights.append(&mut token.expand())
2042 }
2043 if rights.is_empty() {
2044 return Err(Error::missing_rights(format!(
2045 "Rights provided to `{}` are not well formed.",
2046 keyword
2047 )));
2048 }
2049 let mut seen_rights = BTreeSet::new();
2050 let mut operations: fio::Operations = fio::Operations::empty();
2051 for right in rights.iter() {
2052 if seen_rights.contains(&right) {
2053 return Err(Error::duplicate_rights(format!(
2054 "Rights provided to `{}` are not well formed.",
2055 keyword
2056 )));
2057 }
2058 seen_rights.insert(right);
2059 operations |= *right;
2060 }
2061
2062 Ok(operations)
2063 }
2064 None => Err(Error::internal(format!(
2065 "No `{}` rights provided but required for directories",
2066 keyword
2067 ))),
2068 }
2069}
2070
2071pub fn any_ref_to_decl(
2074 options: &CompileOptions<'_>,
2075 reference: AnyRef<'_>,
2076 all_capability_names: Option<&BTreeSet<&Name>>,
2077 all_collection_names: Option<&BTreeSet<&Name>>,
2078) -> Result<(fdecl::Ref, Option<String>), Error> {
2079 let ref_ = match reference {
2080 AnyRef::Named(name) => {
2081 if all_capability_names.is_some() && all_capability_names.unwrap().contains(&name) {
2082 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
2083 } else if all_collection_names.is_some()
2084 && all_collection_names.unwrap().contains(&name)
2085 {
2086 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() })
2087 } else {
2088 fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
2089 }
2090 }
2091 AnyRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2092 AnyRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
2093 AnyRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
2094 AnyRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
2095 AnyRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
2096 AnyRef::Dictionary(d) => {
2097 return dictionary_ref_to_source(&d);
2098 }
2099 AnyRef::OwnDictionary(name) => {
2100 #[cfg(fuchsia_api_level_at_least = "25")]
2101 {
2102 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
2103 }
2104 #[cfg(fuchsia_api_level_less_than = "25")]
2105 return Err(Error::validate("dictionaries are not supported at this API level"));
2106 }
2107 };
2108 Ok((ref_, None))
2109}
2110
2111fn dictionary_ref_to_source(d: &DictionaryRef) -> Result<(fdecl::Ref, Option<String>), Error> {
2113 #[allow(unused)]
2114 let root = match &d.root {
2115 RootDictionaryRef::Named(name) => {
2116 fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
2117 }
2118 RootDictionaryRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
2119 RootDictionaryRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
2120 };
2121
2122 #[cfg(fuchsia_api_level_at_least = "25")]
2123 return Ok((root, Some(d.path.to_string())));
2124 #[cfg(fuchsia_api_level_less_than = "25")]
2125 return Err(Error::validate("dictionaries are not supported at this API level"));
2126}
2127
2128fn configuration_to_value(
2129 name: &Name,
2130 capability: &Capability,
2131 config_type: &Option<ConfigType>,
2132 value: &Option<serde_json::Value>,
2133) -> Result<fdecl::ConfigValue, Error> {
2134 let Some(config_type) = config_type.as_ref() else {
2135 return Err(Error::InvalidArgs(format!(
2136 "Configuration field '{}' must have 'type' set",
2137 name
2138 )));
2139 };
2140 let Some(value) = value.as_ref() else {
2141 return Err(Error::InvalidArgs(format!(
2142 "Configuration field '{}' must have 'value' set",
2143 name
2144 )));
2145 };
2146
2147 let config_type = match config_type {
2148 ConfigType::Bool => cm_rust::ConfigValueType::Bool,
2149 ConfigType::Uint8 => cm_rust::ConfigValueType::Uint8,
2150 ConfigType::Uint16 => cm_rust::ConfigValueType::Uint16,
2151 ConfigType::Uint32 => cm_rust::ConfigValueType::Uint32,
2152 ConfigType::Uint64 => cm_rust::ConfigValueType::Uint64,
2153 ConfigType::Int8 => cm_rust::ConfigValueType::Int8,
2154 ConfigType::Int16 => cm_rust::ConfigValueType::Int16,
2155 ConfigType::Int32 => cm_rust::ConfigValueType::Int32,
2156 ConfigType::Int64 => cm_rust::ConfigValueType::Int64,
2157 ConfigType::String => {
2158 let Some(max_size) = capability.config_max_size else {
2159 return Err(Error::InvalidArgs(format!(
2160 "Configuration field '{}' must have 'max_size' set",
2161 name
2162 )));
2163 };
2164 cm_rust::ConfigValueType::String { max_size: max_size.into() }
2165 }
2166 ConfigType::Vector => {
2167 let Some(ref element) = capability.config_element_type else {
2168 return Err(Error::InvalidArgs(format!(
2169 "Configuration field '{}' must have 'element_type' set",
2170 name
2171 )));
2172 };
2173 let Some(max_count) = capability.config_max_count else {
2174 return Err(Error::InvalidArgs(format!(
2175 "Configuration field '{}' must have 'max_count' set",
2176 name
2177 )));
2178 };
2179 let nested_type = match element {
2180 ConfigNestedValueType::Bool { .. } => cm_rust::ConfigNestedValueType::Bool,
2181 ConfigNestedValueType::Uint8 { .. } => cm_rust::ConfigNestedValueType::Uint8,
2182 ConfigNestedValueType::Uint16 { .. } => cm_rust::ConfigNestedValueType::Uint16,
2183 ConfigNestedValueType::Uint32 { .. } => cm_rust::ConfigNestedValueType::Uint32,
2184 ConfigNestedValueType::Uint64 { .. } => cm_rust::ConfigNestedValueType::Uint64,
2185 ConfigNestedValueType::Int8 { .. } => cm_rust::ConfigNestedValueType::Int8,
2186 ConfigNestedValueType::Int16 { .. } => cm_rust::ConfigNestedValueType::Int16,
2187 ConfigNestedValueType::Int32 { .. } => cm_rust::ConfigNestedValueType::Int32,
2188 ConfigNestedValueType::Int64 { .. } => cm_rust::ConfigNestedValueType::Int64,
2189 ConfigNestedValueType::String { max_size } => {
2190 cm_rust::ConfigNestedValueType::String { max_size: (*max_size).into() }
2191 }
2192 };
2193 cm_rust::ConfigValueType::Vector { max_count: max_count.into(), nested_type }
2194 }
2195 };
2196 let value = config_value_file::field::config_value_from_json_value(value, &config_type)
2197 .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", name, e)))?;
2198 Ok(value.native_into_fidl())
2199}
2200
2201#[cfg(test)]
2202pub mod test_util {
2203 macro_rules! must_parse_cml {
2205 ($($input:tt)+) => {
2206 serde_json::from_str::<Document>(&json!($($input)+).to_string())
2207 .expect("deserialization failed")
2208 };
2209 }
2210 pub(crate) use must_parse_cml;
2211}
2212
2213#[cfg(test)]
2214mod tests {
2215 use super::*;
2216 use crate::error::Error;
2217 use crate::features::Feature;
2218 use crate::translate::test_util::must_parse_cml;
2219 use crate::{
2220 create_offer, AnyRef, AsClause, Capability, CapabilityClause, Child, Collection,
2221 DebugRegistration, Document, Environment, EnvironmentExtends, EnvironmentRef, Expose,
2222 ExposeFromRef, ExposeToRef, FromClause, Offer, OfferFromRef, OneOrMany, Path, PathClause,
2223 Program, ResolverRegistration, RightsClause, RunnerRegistration, Use, UseFromRef,
2224 };
2225 use assert_matches::assert_matches;
2226 use cm_fidl_validator::error::{AvailabilityList, DeclField, Error as CmFidlError, ErrorList};
2227 use cm_types::{self as cm, Name};
2228 use difference::Changeset;
2229 use serde_json::{json, Map, Value};
2230 use std::collections::BTreeSet;
2231 use std::convert::Into;
2232 use std::str::FromStr;
2233 use {
2234 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
2235 };
2236
2237 macro_rules! test_compile {
2238 (
2239 $(
2240 $(#[$m:meta])*
2241 $test_name:ident => {
2242 $(features = $features:expr,)?
2243 input = $input:expr,
2244 output = $expected:expr,
2245 },
2246 )+
2247 ) => {
2248 $(
2249 $(#[$m])*
2250 #[test]
2251 fn $test_name() {
2252 let input = serde_json::from_str(&$input.to_string()).expect("deserialization failed");
2253 let options = CompileOptions::new().config_package_path("fake.cvf");
2254 $(let features = $features; let options = options.features(&features);)?
2256 let actual = compile(&input, options).expect("compilation failed");
2257 if actual != $expected {
2258 let e = format!("{:#?}", $expected);
2259 let a = format!("{:#?}", actual);
2260 panic!("{}", Changeset::new(&a, &e, "\n"));
2261 }
2262 }
2263 )+
2264 };
2265 }
2266
2267 fn default_component_decl() -> fdecl::Component {
2268 fdecl::Component::default()
2269 }
2270
2271 test_compile! {
2272 test_compile_empty => {
2273 input = json!({}),
2274 output = default_component_decl(),
2275 },
2276
2277 test_compile_empty_includes => {
2278 input = json!({ "include": [] }),
2279 output = default_component_decl(),
2280 },
2281
2282 test_compile_offer_to_all_and_diff_sources => {
2283 input = json!({
2284 "children": [
2285 {
2286 "name": "logger",
2287 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
2288 },
2289 ],
2290 "collections": [
2291 {
2292 "name": "coll",
2293 "durability": "transient",
2294 },
2295 ],
2296 "offer": [
2297 {
2298 "protocol": "fuchsia.logger.LogSink",
2299 "from": "parent",
2300 "to": "all",
2301 },
2302 {
2303 "protocol": "fuchsia.logger.LogSink",
2304 "from": "framework",
2305 "to": "#logger",
2306 "as": "LogSink2",
2307 },
2308 ],
2309 }),
2310 output = fdecl::Component {
2311 offers: Some(vec![
2312 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2313 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
2314 source_name: Some("fuchsia.logger.LogSink".into()),
2315 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2316 name: "logger".into(),
2317 collection: None,
2318 })),
2319 target_name: Some("LogSink2".into()),
2320 dependency_type: Some(fdecl::DependencyType::Strong),
2321 availability: Some(fdecl::Availability::Required),
2322 ..Default::default()
2323 }),
2324 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2325 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2326 source_name: Some("fuchsia.logger.LogSink".into()),
2327 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2328 name: "logger".into(),
2329 collection: None,
2330 })),
2331 target_name: Some("fuchsia.logger.LogSink".into()),
2332 dependency_type: Some(fdecl::DependencyType::Strong),
2333 availability: Some(fdecl::Availability::Required),
2334 ..Default::default()
2335 }),
2336 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2337 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2338 source_name: Some("fuchsia.logger.LogSink".into()),
2339 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
2340 name: "coll".into(),
2341 })),
2342 target_name: Some("fuchsia.logger.LogSink".into()),
2343 dependency_type: Some(fdecl::DependencyType::Strong),
2344 availability: Some(fdecl::Availability::Required),
2345 ..Default::default()
2346 }),
2347 ]),
2348 children: Some(vec![fdecl::Child {
2349 name: Some("logger".into()),
2350 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
2351 startup: Some(fdecl::StartupMode::Lazy),
2352 ..Default::default()
2353 }]),
2354 collections: Some(vec![fdecl::Collection {
2355 name: Some("coll".into()),
2356 durability: Some(fdecl::Durability::Transient),
2357 ..Default::default()
2358 }]),
2359 ..default_component_decl()
2360 },
2361 },
2362
2363 test_compile_offer_to_all => {
2364 input = json!({
2365 "children": [
2366 {
2367 "name": "logger",
2368 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
2369 },
2370 {
2371 "name": "something",
2372 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
2373 },
2374 ],
2375 "collections": [
2376 {
2377 "name": "coll",
2378 "durability": "transient",
2379 },
2380 ],
2381 "offer": [
2382 {
2383 "protocol": "fuchsia.logger.LogSink",
2384 "from": "parent",
2385 "to": "all",
2386 },
2387 {
2388 "protocol": "fuchsia.inspect.InspectSink",
2389 "from": "parent",
2390 "to": "all",
2391 },
2392 {
2393 "protocol": "fuchsia.logger.LegacyLog",
2394 "from": "parent",
2395 "to": "#logger",
2396 },
2397 ],
2398 }),
2399 output = fdecl::Component {
2400 offers: Some(vec![
2401 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2402 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2403 source_name: Some("fuchsia.logger.LegacyLog".into()),
2404 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2405 name: "logger".into(),
2406 collection: None,
2407 })),
2408 target_name: Some("fuchsia.logger.LegacyLog".into()),
2409 dependency_type: Some(fdecl::DependencyType::Strong),
2410 availability: Some(fdecl::Availability::Required),
2411 ..Default::default()
2412 }),
2413 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2414 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2415 source_name: Some("fuchsia.logger.LogSink".into()),
2416 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2417 name: "logger".into(),
2418 collection: None,
2419 })),
2420 target_name: Some("fuchsia.logger.LogSink".into()),
2421 dependency_type: Some(fdecl::DependencyType::Strong),
2422 availability: Some(fdecl::Availability::Required),
2423 ..Default::default()
2424 }),
2425 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2426 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2427 source_name: Some("fuchsia.logger.LogSink".into()),
2428 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2429 name: "something".into(),
2430 collection: None,
2431 })),
2432 target_name: Some("fuchsia.logger.LogSink".into()),
2433 dependency_type: Some(fdecl::DependencyType::Strong),
2434 availability: Some(fdecl::Availability::Required),
2435 ..Default::default()
2436 }),
2437 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2438 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2439 source_name: Some("fuchsia.logger.LogSink".into()),
2440 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
2441 name: "coll".into(),
2442 })),
2443 target_name: Some("fuchsia.logger.LogSink".into()),
2444 dependency_type: Some(fdecl::DependencyType::Strong),
2445 availability: Some(fdecl::Availability::Required),
2446 ..Default::default()
2447 }),
2448 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2449 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2450 source_name: Some("fuchsia.inspect.InspectSink".into()),
2451 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2452 name: "logger".into(),
2453 collection: None,
2454 })),
2455 target_name: Some("fuchsia.inspect.InspectSink".into()),
2456 dependency_type: Some(fdecl::DependencyType::Strong),
2457 availability: Some(fdecl::Availability::Required),
2458 ..Default::default()
2459 }),
2460 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2461 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2462 source_name: Some("fuchsia.inspect.InspectSink".into()),
2463 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2464 name: "something".into(),
2465 collection: None,
2466 })),
2467 target_name: Some("fuchsia.inspect.InspectSink".into()),
2468 dependency_type: Some(fdecl::DependencyType::Strong),
2469 availability: Some(fdecl::Availability::Required),
2470 ..Default::default()
2471 }),
2472 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2473 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2474 source_name: Some("fuchsia.inspect.InspectSink".into()),
2475 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
2476 name: "coll".into(),
2477 })),
2478 target_name: Some("fuchsia.inspect.InspectSink".into()),
2479 dependency_type: Some(fdecl::DependencyType::Strong),
2480 availability: Some(fdecl::Availability::Required),
2481 ..Default::default()
2482 }),
2483 ]),
2484 children: Some(vec![
2485 fdecl::Child {
2486 name: Some("logger".into()),
2487 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
2488 startup: Some(fdecl::StartupMode::Lazy),
2489 ..Default::default()
2490 },
2491 fdecl::Child {
2492 name: Some("something".into()),
2493 url: Some(
2494 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
2495 ),
2496 startup: Some(fdecl::StartupMode::Lazy),
2497 ..Default::default()
2498 },
2499 ]),
2500 collections: Some(vec![fdecl::Collection {
2501 name: Some("coll".into()),
2502 durability: Some(fdecl::Durability::Transient),
2503 ..Default::default()
2504 }]),
2505 ..default_component_decl()
2506 },
2507 },
2508
2509 test_compile_offer_to_all_hides_individual_duplicate_routes => {
2510 input = json!({
2511 "children": [
2512 {
2513 "name": "logger",
2514 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
2515 },
2516 {
2517 "name": "something",
2518 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
2519 },
2520 {
2521 "name": "something-v2",
2522 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
2523 },
2524 ],
2525 "collections": [
2526 {
2527 "name": "coll",
2528 "durability": "transient",
2529 },
2530 {
2531 "name": "coll2",
2532 "durability": "transient",
2533 },
2534 ],
2535 "offer": [
2536 {
2537 "protocol": "fuchsia.logger.LogSink",
2538 "from": "parent",
2539 "to": "#logger",
2540 },
2541 {
2542 "protocol": "fuchsia.logger.LogSink",
2543 "from": "parent",
2544 "to": "all",
2545 },
2546 {
2547 "protocol": "fuchsia.logger.LogSink",
2548 "from": "parent",
2549 "to": [ "#something", "#something-v2", "#coll2"],
2550 },
2551 {
2552 "protocol": "fuchsia.logger.LogSink",
2553 "from": "parent",
2554 "to": "#coll",
2555 },
2556 ],
2557 }),
2558 output = fdecl::Component {
2559 offers: Some(vec![
2560 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2561 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2562 source_name: Some("fuchsia.logger.LogSink".into()),
2563 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2564 name: "logger".into(),
2565 collection: None,
2566 })),
2567 target_name: Some("fuchsia.logger.LogSink".into()),
2568 dependency_type: Some(fdecl::DependencyType::Strong),
2569 availability: Some(fdecl::Availability::Required),
2570 ..Default::default()
2571 }),
2572 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2573 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2574 source_name: Some("fuchsia.logger.LogSink".into()),
2575 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2576 name: "something".into(),
2577 collection: None,
2578 })),
2579 target_name: Some("fuchsia.logger.LogSink".into()),
2580 dependency_type: Some(fdecl::DependencyType::Strong),
2581 availability: Some(fdecl::Availability::Required),
2582 ..Default::default()
2583 }),
2584 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2585 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2586 source_name: Some("fuchsia.logger.LogSink".into()),
2587 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2588 name: "something-v2".into(),
2589 collection: None,
2590 })),
2591 target_name: Some("fuchsia.logger.LogSink".into()),
2592 dependency_type: Some(fdecl::DependencyType::Strong),
2593 availability: Some(fdecl::Availability::Required),
2594 ..Default::default()
2595 }),
2596 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2597 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2598 source_name: Some("fuchsia.logger.LogSink".into()),
2599 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
2600 name: "coll2".into(),
2601 })),
2602 target_name: Some("fuchsia.logger.LogSink".into()),
2603 dependency_type: Some(fdecl::DependencyType::Strong),
2604 availability: Some(fdecl::Availability::Required),
2605 ..Default::default()
2606 }),
2607 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2608 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2609 source_name: Some("fuchsia.logger.LogSink".into()),
2610 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
2611 name: "coll".into(),
2612 })),
2613 target_name: Some("fuchsia.logger.LogSink".into()),
2614 dependency_type: Some(fdecl::DependencyType::Strong),
2615 availability: Some(fdecl::Availability::Required),
2616 ..Default::default()
2617 }),
2618 ]),
2619 children: Some(vec![
2620 fdecl::Child {
2621 name: Some("logger".into()),
2622 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
2623 startup: Some(fdecl::StartupMode::Lazy),
2624 ..Default::default()
2625 },
2626 fdecl::Child {
2627 name: Some("something".into()),
2628 url: Some(
2629 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
2630 ),
2631 startup: Some(fdecl::StartupMode::Lazy),
2632 ..Default::default()
2633 },
2634 fdecl::Child {
2635 name: Some("something-v2".into()),
2636 url: Some(
2637 "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
2638 ),
2639 startup: Some(fdecl::StartupMode::Lazy),
2640 ..Default::default()
2641 },
2642 ]),
2643 collections: Some(vec![fdecl::Collection {
2644 name: Some("coll".into()),
2645 durability: Some(fdecl::Durability::Transient),
2646 ..Default::default()
2647 }, fdecl::Collection {
2648 name: Some("coll2".into()),
2649 durability: Some(fdecl::Durability::Transient),
2650 ..Default::default()
2651 }]),
2652 ..default_component_decl()
2653 },
2654 },
2655
2656 test_compile_offer_to_all_from_child => {
2657 input = json!({
2658 "children": [
2659 {
2660 "name": "logger",
2661 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
2662 },
2663 {
2664 "name": "something",
2665 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
2666 },
2667 {
2668 "name": "something-v2",
2669 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
2670 },
2671 ],
2672 "offer": [
2673 {
2674 "protocol": "fuchsia.logger.LogSink",
2675 "from": "#logger",
2676 "to": "all",
2677 },
2678 ],
2679 }),
2680 output = fdecl::Component {
2681 offers: Some(vec![
2682 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2683 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2684 name: "logger".into(),
2685 collection: None,
2686 })),
2687 source_name: Some("fuchsia.logger.LogSink".into()),
2688 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2689 name: "something".into(),
2690 collection: None,
2691 })),
2692 target_name: Some("fuchsia.logger.LogSink".into()),
2693 dependency_type: Some(fdecl::DependencyType::Strong),
2694 availability: Some(fdecl::Availability::Required),
2695 ..Default::default()
2696 }),
2697 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2698 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
2699 name: "logger".into(),
2700 collection: None,
2701 })),
2702 source_name: Some("fuchsia.logger.LogSink".into()),
2703 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2704 name: "something-v2".into(),
2705 collection: None,
2706 })),
2707 target_name: Some("fuchsia.logger.LogSink".into()),
2708 dependency_type: Some(fdecl::DependencyType::Strong),
2709 availability: Some(fdecl::Availability::Required),
2710 ..Default::default()
2711 }),
2712 ]),
2713 children: Some(vec![
2714 fdecl::Child {
2715 name: Some("logger".into()),
2716 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
2717 startup: Some(fdecl::StartupMode::Lazy),
2718 ..Default::default()
2719 },
2720 fdecl::Child {
2721 name: Some("something".into()),
2722 url: Some(
2723 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
2724 ),
2725 startup: Some(fdecl::StartupMode::Lazy),
2726 ..Default::default()
2727 },
2728 fdecl::Child {
2729 name: Some("something-v2".into()),
2730 url: Some(
2731 "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
2732 ),
2733 startup: Some(fdecl::StartupMode::Lazy),
2734 ..Default::default()
2735 },
2736 ]),
2737 ..default_component_decl()
2738 },
2739 },
2740
2741 test_compile_offer_multiple_protocols_to_single_array_syntax_and_all => {
2742 input = json!({
2743 "children": [
2744 {
2745 "name": "something",
2746 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
2747 },
2748 ],
2749 "offer": [
2750 {
2751 "protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
2752 "from": "parent",
2753 "to": "#something",
2754 },
2755 {
2756 "protocol": "fuchsia.logger.LogSink",
2757 "from": "parent",
2758 "to": "all",
2759 },
2760 ],
2761 }),
2762 output = fdecl::Component {
2763 offers: Some(vec![
2764 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2765 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2766 source_name: Some("fuchsia.logger.LogSink".into()),
2767 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2768 name: "something".into(),
2769 collection: None,
2770 })),
2771 target_name: Some("fuchsia.logger.LogSink".into()),
2772 dependency_type: Some(fdecl::DependencyType::Strong),
2773 availability: Some(fdecl::Availability::Required),
2774 ..Default::default()
2775 }),
2776 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2777 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2778 source_name: Some("fuchsia.inspect.InspectSink".into()),
2779 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2780 name: "something".into(),
2781 collection: None,
2782 })),
2783 target_name: Some("fuchsia.inspect.InspectSink".into()),
2784 dependency_type: Some(fdecl::DependencyType::Strong),
2785 availability: Some(fdecl::Availability::Required),
2786 ..Default::default()
2787 }),
2788 ]),
2789 children: Some(vec![
2790 fdecl::Child {
2791 name: Some("something".into()),
2792 url: Some(
2793 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
2794 ),
2795 startup: Some(fdecl::StartupMode::Lazy),
2796 ..Default::default()
2797 },
2798 ]),
2799 ..default_component_decl()
2800 },
2801 },
2802
2803 test_compile_offer_to_all_array_and_single => {
2804 input = json!({
2805 "children": [
2806 {
2807 "name": "something",
2808 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
2809 },
2810 ],
2811 "offer": [
2812 {
2813 "protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
2814 "from": "parent",
2815 "to": "all",
2816 },
2817 {
2818 "protocol": "fuchsia.logger.LogSink",
2819 "from": "parent",
2820 "to": "#something",
2821 },
2822 ],
2823 }),
2824 output = fdecl::Component {
2825 offers: Some(vec![
2826 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2827 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2828 source_name: Some("fuchsia.logger.LogSink".into()),
2829 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2830 name: "something".into(),
2831 collection: None,
2832 })),
2833 target_name: Some("fuchsia.logger.LogSink".into()),
2834 dependency_type: Some(fdecl::DependencyType::Strong),
2835 availability: Some(fdecl::Availability::Required),
2836 ..Default::default()
2837 }),
2838 fdecl::Offer::Protocol(fdecl::OfferProtocol {
2839 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2840 source_name: Some("fuchsia.inspect.InspectSink".into()),
2841 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
2842 name: "something".into(),
2843 collection: None,
2844 })),
2845 target_name: Some("fuchsia.inspect.InspectSink".into()),
2846 dependency_type: Some(fdecl::DependencyType::Strong),
2847 availability: Some(fdecl::Availability::Required),
2848 ..Default::default()
2849 }),
2850 ]),
2851 children: Some(vec![
2852 fdecl::Child {
2853 name: Some("something".into()),
2854 url: Some(
2855 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
2856 ),
2857 startup: Some(fdecl::StartupMode::Lazy),
2858 ..Default::default()
2859 },
2860 ]),
2861 ..default_component_decl()
2862 },
2863 },
2864
2865 test_compile_program => {
2866 input = json!({
2867 "program": {
2868 "runner": "elf",
2869 "binary": "bin/app",
2870 },
2871 }),
2872 output = fdecl::Component {
2873 program: Some(fdecl::Program {
2874 runner: Some("elf".to_string()),
2875 info: Some(fdata::Dictionary {
2876 entries: Some(vec![fdata::DictionaryEntry {
2877 key: "binary".to_string(),
2878 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
2879 }]),
2880 ..Default::default()
2881 }),
2882 ..Default::default()
2883 }),
2884 ..default_component_decl()
2885 },
2886 },
2887
2888 test_compile_program_with_use_runner => {
2889 input = json!({
2890 "program": {
2891 "binary": "bin/app",
2892 },
2893 "use": [
2894 { "runner": "elf", "from": "parent", },
2895 ],
2896 }),
2897 output = fdecl::Component {
2898 program: Some(fdecl::Program {
2899 runner: None,
2900 info: Some(fdata::Dictionary {
2901 entries: Some(vec![fdata::DictionaryEntry {
2902 key: "binary".to_string(),
2903 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
2904 }]),
2905 ..Default::default()
2906 }),
2907 ..Default::default()
2908 }),
2909 uses: Some(vec![
2910 fdecl::Use::Runner (
2911 fdecl::UseRunner {
2912 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
2913 source_name: Some("elf".to_string()),
2914 ..Default::default()
2915 }
2916 ),
2917 ]),
2918 ..default_component_decl()
2919 },
2920 },
2921
2922 test_compile_program_with_nested_objects => {
2923 input = json!({
2924 "program": {
2925 "runner": "elf",
2926 "binary": "bin/app",
2927 "one": {
2928 "two": {
2929 "three.four": {
2930 "five": "six"
2931 }
2932 },
2933 }
2934 },
2935 }),
2936 output = fdecl::Component {
2937 program: Some(fdecl::Program {
2938 runner: Some("elf".to_string()),
2939 info: Some(fdata::Dictionary {
2940 entries: Some(vec![
2941 fdata::DictionaryEntry {
2942 key: "binary".to_string(),
2943 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
2944 },
2945 fdata::DictionaryEntry {
2946 key: "one.two.three.four.five".to_string(),
2947 value: Some(Box::new(fdata::DictionaryValue::Str("six".to_string()))),
2948 },
2949 ]),
2950 ..Default::default()
2951 }),
2952 ..Default::default()
2953 }),
2954 ..default_component_decl()
2955 },
2956 },
2957
2958 test_compile_program_with_array_of_objects => {
2959 input = json!({
2960 "program": {
2961 "runner": "elf",
2962 "binary": "bin/app",
2963 "networks": [
2964 {
2965 "endpoints": [
2966 {
2967 "name": "device",
2968 "mac": "aa:bb:cc:dd:ee:ff"
2969 },
2970 {
2971 "name": "emu",
2972 "mac": "ff:ee:dd:cc:bb:aa"
2973 },
2974 ],
2975 "name": "external_network"
2976 }
2977 ],
2978 },
2979 }),
2980 output = fdecl::Component {
2981 program: Some(fdecl::Program {
2982 runner: Some("elf".to_string()),
2983 info: Some(fdata::Dictionary {
2984 entries: Some(vec![
2985 fdata::DictionaryEntry {
2986 key: "binary".to_string(),
2987 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
2988 },
2989 fdata::DictionaryEntry {
2990 key: "networks".to_string(),
2991 value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
2992 fdata::Dictionary {
2993 entries: Some(vec![
2994 fdata::DictionaryEntry {
2995 key: "endpoints".to_string(),
2996 value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
2997 fdata::Dictionary {
2998 entries: Some(vec![
2999 fdata::DictionaryEntry {
3000 key: "mac".to_string(),
3001 value: Some(Box::new(fdata::DictionaryValue::Str("aa:bb:cc:dd:ee:ff".to_string()))),
3002 },
3003 fdata::DictionaryEntry {
3004 key: "name".to_string(),
3005 value: Some(Box::new(fdata::DictionaryValue::Str("device".to_string()))),
3006 }
3007 ]),
3008 ..Default::default()
3009 },
3010 fdata::Dictionary {
3011 entries: Some(vec![
3012 fdata::DictionaryEntry {
3013 key: "mac".to_string(),
3014 value: Some(Box::new(fdata::DictionaryValue::Str("ff:ee:dd:cc:bb:aa".to_string()))),
3015 },
3016 fdata::DictionaryEntry {
3017 key: "name".to_string(),
3018 value: Some(Box::new(fdata::DictionaryValue::Str("emu".to_string()))),
3019 }
3020 ]),
3021 ..Default::default()
3022 },
3023 ])))
3024 },
3025 fdata::DictionaryEntry {
3026 key: "name".to_string(),
3027 value: Some(Box::new(fdata::DictionaryValue::Str("external_network".to_string()))),
3028 },
3029 ]),
3030 ..Default::default()
3031 }
3032 ]))),
3033 },
3034 ]),
3035 ..Default::default()
3036 }),
3037 ..Default::default()
3038 }),
3039 ..default_component_decl()
3040 },
3041 },
3042
3043 test_compile_use => {
3044 input = json!({
3045 "use": [
3046 {
3047 "protocol": "LegacyCoolFonts",
3048 "path": "/svc/fuchsia.fonts.LegacyProvider",
3049 "availability": "optional",
3050 },
3051 { "protocol": "fuchsia.sys2.LegacyRealm", "from": "framework" },
3052 { "protocol": "fuchsia.sys2.StorageAdmin", "from": "#data-storage" },
3053 { "protocol": "fuchsia.sys2.DebugProto", "from": "debug" },
3054 { "protocol": "fuchsia.sys2.DictionaryProto", "from": "#logger/in/dict" },
3055 { "protocol": "fuchsia.sys2.Echo", "from": "self", "availability": "transitional" },
3056 { "service": "fuchsia.sys2.EchoService", "from": "parent/dict", },
3057 { "directory": "assets", "rights" : ["read_bytes"], "path": "/data/assets" },
3058 {
3059 "directory": "config",
3060 "path": "/data/config",
3061 "from": "parent",
3062 "rights": ["read_bytes"],
3063 "subdir": "fonts",
3064 },
3065 { "storage": "hippos", "path": "/hippos" },
3066 { "storage": "cache", "path": "/tmp" },
3067 {
3068 "event_stream": "bar_stream",
3069 },
3070 {
3071 "event_stream": ["foobar", "stream"],
3072 "scope": ["#logger", "#modular"],
3073 "path": "/event_stream/another",
3074 },
3075 { "runner": "usain", "from": "parent", },
3076 ],
3077 "capabilities": [
3078 { "protocol": "fuchsia.sys2.Echo" },
3079 {
3080 "config": "fuchsia.config.Config",
3081 "type": "bool",
3082 "value": true,
3083 },
3084 {
3085 "storage": "data-storage",
3086 "from": "parent",
3087 "backing_dir": "minfs",
3088 "storage_id": "static_instance_id_or_moniker",
3089 }
3090 ],
3091 "children": [
3092 {
3093 "name": "logger",
3094 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
3095 "environment": "#env_one"
3096 }
3097 ],
3098 "collections": [
3099 {
3100 "name": "modular",
3101 "durability": "transient",
3102 },
3103 ],
3104 "environments": [
3105 {
3106 "name": "env_one",
3107 "extends": "realm",
3108 }
3109 ]
3110 }),
3111 output = fdecl::Component {
3112 uses: Some(vec![
3113 fdecl::Use::Protocol (
3114 fdecl::UseProtocol {
3115 dependency_type: Some(fdecl::DependencyType::Strong),
3116 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3117 source_name: Some("LegacyCoolFonts".to_string()),
3118 target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
3119 availability: Some(fdecl::Availability::Optional),
3120 ..Default::default()
3121 }
3122 ),
3123 fdecl::Use::Protocol (
3124 fdecl::UseProtocol {
3125 dependency_type: Some(fdecl::DependencyType::Strong),
3126 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
3127 source_name: Some("fuchsia.sys2.LegacyRealm".to_string()),
3128 target_path: Some("/svc/fuchsia.sys2.LegacyRealm".to_string()),
3129 availability: Some(fdecl::Availability::Required),
3130 ..Default::default()
3131 }
3132 ),
3133 fdecl::Use::Protocol (
3134 fdecl::UseProtocol {
3135 dependency_type: Some(fdecl::DependencyType::Strong),
3136 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data-storage".to_string() })),
3137 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
3138 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
3139 availability: Some(fdecl::Availability::Required),
3140 ..Default::default()
3141 }
3142 ),
3143 fdecl::Use::Protocol (
3144 fdecl::UseProtocol {
3145 dependency_type: Some(fdecl::DependencyType::Strong),
3146 source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
3147 source_name: Some("fuchsia.sys2.DebugProto".to_string()),
3148 target_path: Some("/svc/fuchsia.sys2.DebugProto".to_string()),
3149 availability: Some(fdecl::Availability::Required),
3150 ..Default::default()
3151 }
3152 ),
3153 fdecl::Use::Protocol (
3154 fdecl::UseProtocol {
3155 dependency_type: Some(fdecl::DependencyType::Strong),
3156 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3157 name: "logger".into(),
3158 collection: None,
3159 })),
3160 #[cfg(fuchsia_api_level_at_least = "25")]
3161 source_dictionary: Some("in/dict".into()),
3162 source_name: Some("fuchsia.sys2.DictionaryProto".to_string()),
3163 target_path: Some("/svc/fuchsia.sys2.DictionaryProto".to_string()),
3164 availability: Some(fdecl::Availability::Required),
3165 ..Default::default()
3166 }
3167 ),
3168 fdecl::Use::Protocol (
3169 fdecl::UseProtocol {
3170 dependency_type: Some(fdecl::DependencyType::Strong),
3171 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3172 source_name: Some("fuchsia.sys2.Echo".to_string()),
3173 target_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
3174 availability: Some(fdecl::Availability::Transitional),
3175 ..Default::default()
3176 }
3177 ),
3178 fdecl::Use::Service (
3179 fdecl::UseService {
3180 dependency_type: Some(fdecl::DependencyType::Strong),
3181 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3182 #[cfg(fuchsia_api_level_at_least = "25")]
3183 source_dictionary: Some("dict".into()),
3184 source_name: Some("fuchsia.sys2.EchoService".to_string()),
3185 target_path: Some("/svc/fuchsia.sys2.EchoService".to_string()),
3186 availability: Some(fdecl::Availability::Required),
3187 ..Default::default()
3188 }
3189 ),
3190 fdecl::Use::Directory (
3191 fdecl::UseDirectory {
3192 dependency_type: Some(fdecl::DependencyType::Strong),
3193 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3194 source_name: Some("assets".to_string()),
3195 target_path: Some("/data/assets".to_string()),
3196 rights: Some(fio::Operations::READ_BYTES),
3197 subdir: None,
3198 availability: Some(fdecl::Availability::Required),
3199 ..Default::default()
3200 }
3201 ),
3202 fdecl::Use::Directory (
3203 fdecl::UseDirectory {
3204 dependency_type: Some(fdecl::DependencyType::Strong),
3205 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3206 source_name: Some("config".to_string()),
3207 target_path: Some("/data/config".to_string()),
3208 rights: Some(fio::Operations::READ_BYTES),
3209 subdir: Some("fonts".to_string()),
3210 availability: Some(fdecl::Availability::Required),
3211 ..Default::default()
3212 }
3213 ),
3214 fdecl::Use::Storage (
3215 fdecl::UseStorage {
3216 source_name: Some("hippos".to_string()),
3217 target_path: Some("/hippos".to_string()),
3218 availability: Some(fdecl::Availability::Required),
3219 ..Default::default()
3220 }
3221 ),
3222 fdecl::Use::Storage (
3223 fdecl::UseStorage {
3224 source_name: Some("cache".to_string()),
3225 target_path: Some("/tmp".to_string()),
3226 availability: Some(fdecl::Availability::Required),
3227 ..Default::default()
3228 }
3229 ),
3230 fdecl::Use::EventStream(fdecl::UseEventStream {
3231 source_name: Some("bar_stream".to_string()),
3232 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3233 target_path: Some("/svc/fuchsia.component.EventStream".to_string()),
3234 availability: Some(fdecl::Availability::Required),
3235 ..Default::default()
3236 }),
3237 fdecl::Use::EventStream(fdecl::UseEventStream {
3238 source_name: Some("foobar".to_string()),
3239 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
3240 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3241 target_path: Some("/event_stream/another".to_string()),
3242 availability: Some(fdecl::Availability::Required),
3243 ..Default::default()
3244 }),
3245 fdecl::Use::EventStream(fdecl::UseEventStream {
3246 source_name: Some("stream".to_string()),
3247 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
3248 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3249 target_path: Some("/event_stream/another".to_string()),
3250 availability: Some(fdecl::Availability::Required),
3251 ..Default::default()
3252 }),
3253 fdecl::Use::Runner(fdecl::UseRunner {
3254 source_name: Some("usain".to_string()),
3255 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
3256 ..Default::default()
3257 }),
3258 ]),
3259 collections: Some(vec![
3260 fdecl::Collection{
3261 name:Some("modular".to_string()),
3262 durability:Some(fdecl::Durability::Transient),
3263 ..Default::default()
3264 },
3265 ]),
3266 capabilities: Some(vec![
3267 fdecl::Capability::Protocol(fdecl::Protocol {
3268 name: Some("fuchsia.sys2.Echo".to_string()),
3269 source_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
3270 ..Default::default()
3271 }),
3272 fdecl::Capability::Config(fdecl::Configuration {
3273 name: Some("fuchsia.config.Config".to_string()),
3274 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
3275 ..Default::default()
3276 }),
3277 fdecl::Capability::Storage(fdecl::Storage {
3278 name: Some("data-storage".to_string()),
3279 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3280 backing_dir: Some("minfs".to_string()),
3281 subdir: None,
3282 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3283 ..Default::default()
3284 }),
3285 ]),
3286 children: Some(vec![
3287 fdecl::Child{
3288 name:Some("logger".to_string()),
3289 url:Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
3290 startup:Some(fdecl::StartupMode::Lazy),
3291 environment: Some("env_one".to_string()),
3292 ..Default::default()
3293 }
3294 ]),
3295 environments: Some(vec![
3296 fdecl::Environment {
3297 name: Some("env_one".to_string()),
3298 extends: Some(fdecl::EnvironmentExtends::Realm),
3299 ..Default::default()
3300 },
3301 ]),
3302 config: None,
3303 ..default_component_decl()
3304 },
3305 },
3306
3307 test_compile_expose => {
3308 input = json!({
3309 "expose": [
3310 {
3311 "protocol": "fuchsia.logger.Log",
3312 "from": "#logger",
3313 "as": "fuchsia.logger.LegacyLog",
3314 "to": "parent"
3315 },
3316 {
3317 "protocol": [ "A", "B" ],
3318 "from": "self",
3319 "to": "parent"
3320 },
3321 {
3322 "protocol": "C",
3323 "from": "#data-storage",
3324 },
3325 {
3326 "protocol": "D",
3327 "from": "#logger/in/dict",
3328 "as": "E",
3329 },
3330 {
3331 "service": "F",
3332 "from": "#logger/in/dict",
3333 },
3334 {
3335 "service": "svc",
3336 "from": [ "#logger", "#coll", "self" ],
3337 },
3338 {
3339 "directory": "blob",
3340 "from": "self",
3341 "to": "framework",
3342 "rights": ["r*"],
3343 },
3344 {
3345 "directory": [ "blob2", "blob3" ],
3346 "from": "#logger",
3347 "to": "parent",
3348 },
3349 { "directory": "hub", "from": "framework" },
3350 { "runner": "web", "from": "#logger", "to": "parent", "as": "web-rename" },
3351 { "runner": [ "runner_a", "runner_b" ], "from": "#logger" },
3352 { "resolver": "my_resolver", "from": "#logger", "to": "parent", "as": "pkg_resolver" },
3353 { "resolver": [ "resolver_a", "resolver_b" ], "from": "#logger" },
3354 { "dictionary": [ "dictionary_a", "dictionary_b" ], "from": "#logger" },
3355 ],
3356 "capabilities": [
3357 { "protocol": "A" },
3358 { "protocol": "B" },
3359 { "service": "svc" },
3360 {
3361 "directory": "blob",
3362 "path": "/volumes/blobfs/blob",
3363 "rights": ["r*"],
3364 },
3365 {
3366 "runner": "web",
3367 "path": "/svc/fuchsia.component.ComponentRunner",
3368 },
3369 {
3370 "storage": "data-storage",
3371 "from": "parent",
3372 "backing_dir": "minfs",
3373 "storage_id": "static_instance_id_or_moniker",
3374 },
3375 ],
3376 "children": [
3377 {
3378 "name": "logger",
3379 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
3380 },
3381 ],
3382 "collections": [
3383 {
3384 "name": "coll",
3385 "durability": "transient",
3386 },
3387 ],
3388 }),
3389 output = fdecl::Component {
3390 exposes: Some(vec![
3391 fdecl::Expose::Protocol (
3392 fdecl::ExposeProtocol {
3393 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3394 name: "logger".to_string(),
3395 collection: None,
3396 })),
3397 source_name: Some("fuchsia.logger.Log".to_string()),
3398 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3399 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
3400 availability: Some(fdecl::Availability::Required),
3401 ..Default::default()
3402 }
3403 ),
3404 fdecl::Expose::Protocol (
3405 fdecl::ExposeProtocol {
3406 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3407 source_name: Some("A".to_string()),
3408 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3409 target_name: Some("A".to_string()),
3410 availability: Some(fdecl::Availability::Required),
3411 ..Default::default()
3412 }
3413 ),
3414 fdecl::Expose::Protocol (
3415 fdecl::ExposeProtocol {
3416 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3417 source_name: Some("B".to_string()),
3418 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3419 target_name: Some("B".to_string()),
3420 availability: Some(fdecl::Availability::Required),
3421 ..Default::default()
3422 }
3423 ),
3424 fdecl::Expose::Protocol (
3425 fdecl::ExposeProtocol {
3426 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
3427 name: "data-storage".to_string(),
3428 })),
3429 source_name: Some("C".to_string()),
3430 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3431 target_name: Some("C".to_string()),
3432 availability: Some(fdecl::Availability::Required),
3433 ..Default::default()
3434 }
3435 ),
3436 fdecl::Expose::Protocol (
3437 fdecl::ExposeProtocol {
3438 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3439 name: "logger".to_string(),
3440 collection: None,
3441 })),
3442 #[cfg(fuchsia_api_level_at_least = "25")]
3443 source_dictionary: Some("in/dict".into()),
3444 source_name: Some("D".to_string()),
3445 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3446 target_name: Some("E".to_string()),
3447 availability: Some(fdecl::Availability::Required),
3448 ..Default::default()
3449 }
3450 ),
3451 fdecl::Expose::Service (
3452 fdecl::ExposeService {
3453 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3454 name: "logger".into(),
3455 collection: None,
3456 })),
3457 source_name: Some("F".into()),
3458 #[cfg(fuchsia_api_level_at_least = "25")]
3459 source_dictionary: Some("in/dict".into()),
3460 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3461 target_name: Some("F".into()),
3462 availability: Some(fdecl::Availability::Required),
3463 ..Default::default()
3464 }
3465 ),
3466 fdecl::Expose::Service (
3467 fdecl::ExposeService {
3468 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3469 name: "logger".into(),
3470 collection: None,
3471 })),
3472 source_name: Some("svc".into()),
3473 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3474 target_name: Some("svc".into()),
3475 availability: Some(fdecl::Availability::Required),
3476 ..Default::default()
3477 }
3478 ),
3479 fdecl::Expose::Service (
3480 fdecl::ExposeService {
3481 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
3482 name: "coll".into(),
3483 })),
3484 source_name: Some("svc".into()),
3485 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3486 target_name: Some("svc".into()),
3487 availability: Some(fdecl::Availability::Required),
3488 ..Default::default()
3489 }
3490 ),
3491 fdecl::Expose::Service (
3492 fdecl::ExposeService {
3493 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3494 source_name: Some("svc".into()),
3495 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3496 target_name: Some("svc".into()),
3497 availability: Some(fdecl::Availability::Required),
3498 ..Default::default()
3499 }
3500 ),
3501 fdecl::Expose::Directory (
3502 fdecl::ExposeDirectory {
3503 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
3504 source_name: Some("blob".to_string()),
3505 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
3506 target_name: Some("blob".to_string()),
3507 rights: Some(
3508 fio::Operations::CONNECT | fio::Operations::ENUMERATE |
3509 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
3510 fio::Operations::GET_ATTRIBUTES
3511 ),
3512 subdir: None,
3513 availability: Some(fdecl::Availability::Required),
3514 ..Default::default()
3515 }
3516 ),
3517 fdecl::Expose::Directory (
3518 fdecl::ExposeDirectory {
3519 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3520 name: "logger".to_string(),
3521 collection: None,
3522 })),
3523 source_name: Some("blob2".to_string()),
3524 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3525 target_name: Some("blob2".to_string()),
3526 rights: None,
3527 subdir: None,
3528 availability: Some(fdecl::Availability::Required),
3529 ..Default::default()
3530 }
3531 ),
3532 fdecl::Expose::Directory (
3533 fdecl::ExposeDirectory {
3534 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3535 name: "logger".to_string(),
3536 collection: None,
3537 })),
3538 source_name: Some("blob3".to_string()),
3539 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3540 target_name: Some("blob3".to_string()),
3541 rights: None,
3542 subdir: None,
3543 availability: Some(fdecl::Availability::Required),
3544 ..Default::default()
3545 }
3546 ),
3547 fdecl::Expose::Directory (
3548 fdecl::ExposeDirectory {
3549 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
3550 source_name: Some("hub".to_string()),
3551 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3552 target_name: Some("hub".to_string()),
3553 rights: None,
3554 subdir: None,
3555 availability: Some(fdecl::Availability::Required),
3556 ..Default::default()
3557 }
3558 ),
3559 fdecl::Expose::Runner (
3560 fdecl::ExposeRunner {
3561 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3562 name: "logger".to_string(),
3563 collection: None,
3564 })),
3565 source_name: Some("web".to_string()),
3566 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3567 target_name: Some("web-rename".to_string()),
3568 ..Default::default()
3569 }
3570 ),
3571 fdecl::Expose::Runner (
3572 fdecl::ExposeRunner {
3573 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3574 name: "logger".to_string(),
3575 collection: None,
3576 })),
3577 source_name: Some("runner_a".to_string()),
3578 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3579 target_name: Some("runner_a".to_string()),
3580 ..Default::default()
3581 }
3582 ),
3583 fdecl::Expose::Runner (
3584 fdecl::ExposeRunner {
3585 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3586 name: "logger".to_string(),
3587 collection: None,
3588 })),
3589 source_name: Some("runner_b".to_string()),
3590 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3591 target_name: Some("runner_b".to_string()),
3592 ..Default::default()
3593 }
3594 ),
3595 fdecl::Expose::Resolver (
3596 fdecl::ExposeResolver {
3597 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3598 name: "logger".to_string(),
3599 collection: None,
3600 })),
3601 source_name: Some("my_resolver".to_string()),
3602 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3603 target_name: Some("pkg_resolver".to_string()),
3604 ..Default::default()
3605 }
3606 ),
3607 fdecl::Expose::Resolver (
3608 fdecl::ExposeResolver {
3609 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3610 name: "logger".to_string(),
3611 collection: None,
3612 })),
3613 source_name: Some("resolver_a".to_string()),
3614 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3615 target_name: Some("resolver_a".to_string()),
3616 ..Default::default()
3617 }
3618 ),
3619 fdecl::Expose::Resolver (
3620 fdecl::ExposeResolver {
3621 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3622 name: "logger".to_string(),
3623 collection: None,
3624 })),
3625 source_name: Some("resolver_b".to_string()),
3626 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3627 target_name: Some("resolver_b".to_string()),
3628 ..Default::default()
3629 }
3630 ),
3631 fdecl::Expose::Dictionary (
3632 fdecl::ExposeDictionary {
3633 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3634 name: "logger".to_string(),
3635 collection: None,
3636 })),
3637 source_name: Some("dictionary_a".to_string()),
3638 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3639 target_name: Some("dictionary_a".to_string()),
3640 availability: Some(fdecl::Availability::Required),
3641 ..Default::default()
3642 }
3643 ),
3644 fdecl::Expose::Dictionary (
3645 fdecl::ExposeDictionary {
3646 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3647 name: "logger".to_string(),
3648 collection: None,
3649 })),
3650 source_name: Some("dictionary_b".to_string()),
3651 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3652 target_name: Some("dictionary_b".to_string()),
3653 availability: Some(fdecl::Availability::Required),
3654 ..Default::default()
3655 }
3656 ),
3657 ]),
3658 offers: None,
3659 capabilities: Some(vec![
3660 fdecl::Capability::Protocol (
3661 fdecl::Protocol {
3662 name: Some("A".to_string()),
3663 source_path: Some("/svc/A".to_string()),
3664 ..Default::default()
3665 }
3666 ),
3667 fdecl::Capability::Protocol (
3668 fdecl::Protocol {
3669 name: Some("B".to_string()),
3670 source_path: Some("/svc/B".to_string()),
3671 ..Default::default()
3672 }
3673 ),
3674 fdecl::Capability::Service (
3675 fdecl::Service {
3676 name: Some("svc".to_string()),
3677 source_path: Some("/svc/svc".to_string()),
3678 ..Default::default()
3679 }
3680 ),
3681 fdecl::Capability::Directory (
3682 fdecl::Directory {
3683 name: Some("blob".to_string()),
3684 source_path: Some("/volumes/blobfs/blob".to_string()),
3685 rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
3686 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
3687 fio::Operations::GET_ATTRIBUTES
3688 ),
3689 ..Default::default()
3690 }
3691 ),
3692 fdecl::Capability::Runner (
3693 fdecl::Runner {
3694 name: Some("web".to_string()),
3695 source_path: Some("/svc/fuchsia.component.ComponentRunner".to_string()),
3696 ..Default::default()
3697 }
3698 ),
3699 fdecl::Capability::Storage(fdecl::Storage {
3700 name: Some("data-storage".to_string()),
3701 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3702 backing_dir: Some("minfs".to_string()),
3703 subdir: None,
3704 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
3705 ..Default::default()
3706 }),
3707 ]),
3708 children: Some(vec![
3709 fdecl::Child {
3710 name: Some("logger".to_string()),
3711 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
3712 startup: Some(fdecl::StartupMode::Lazy),
3713 ..Default::default()
3714 }
3715 ]),
3716 collections: Some(vec![
3717 fdecl::Collection {
3718 name: Some("coll".to_string()),
3719 durability: Some(fdecl::Durability::Transient),
3720 ..Default::default()
3721 }
3722 ]),
3723 ..default_component_decl()
3724 },
3725 },
3726
3727 test_compile_expose_other_availability => {
3728 input = json!({
3729 "expose": [
3730 {
3731 "protocol": "fuchsia.logger.Log",
3732 "from": "#logger",
3733 "as": "fuchsia.logger.LegacyLog_default",
3734 "to": "parent"
3735 },
3736 {
3737 "protocol": "fuchsia.logger.Log",
3738 "from": "#logger",
3739 "as": "fuchsia.logger.LegacyLog_required",
3740 "to": "parent",
3741 "availability": "required"
3742 },
3743 {
3744 "protocol": "fuchsia.logger.Log",
3745 "from": "#logger",
3746 "as": "fuchsia.logger.LegacyLog_optional",
3747 "to": "parent",
3748 "availability": "optional"
3749 },
3750 {
3751 "protocol": "fuchsia.logger.Log",
3752 "from": "#logger",
3753 "as": "fuchsia.logger.LegacyLog_same_as_target",
3754 "to": "parent",
3755 "availability": "same_as_target"
3756 },
3757 {
3758 "protocol": "fuchsia.logger.Log",
3759 "from": "#logger",
3760 "as": "fuchsia.logger.LegacyLog_transitional",
3761 "to": "parent",
3762 "availability": "transitional"
3763 },
3764 ],
3765 "children": [
3766 {
3767 "name": "logger",
3768 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
3769 },
3770 ],
3771 }),
3772 output = fdecl::Component {
3773 exposes: Some(vec![
3774 fdecl::Expose::Protocol (
3775 fdecl::ExposeProtocol {
3776 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3777 name: "logger".to_string(),
3778 collection: None,
3779 })),
3780 source_name: Some("fuchsia.logger.Log".to_string()),
3781 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3782 target_name: Some("fuchsia.logger.LegacyLog_default".to_string()),
3783 availability: Some(fdecl::Availability::Required),
3784 ..Default::default()
3785 }
3786 ),
3787 fdecl::Expose::Protocol (
3788 fdecl::ExposeProtocol {
3789 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3790 name: "logger".to_string(),
3791 collection: None,
3792 })),
3793 source_name: Some("fuchsia.logger.Log".to_string()),
3794 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3795 target_name: Some("fuchsia.logger.LegacyLog_required".to_string()),
3796 availability: Some(fdecl::Availability::Required),
3797 ..Default::default()
3798 }
3799 ),
3800 fdecl::Expose::Protocol (
3801 fdecl::ExposeProtocol {
3802 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3803 name: "logger".to_string(),
3804 collection: None,
3805 })),
3806 source_name: Some("fuchsia.logger.Log".to_string()),
3807 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3808 target_name: Some("fuchsia.logger.LegacyLog_optional".to_string()),
3809 availability: Some(fdecl::Availability::Optional),
3810 ..Default::default()
3811 }
3812 ),
3813 fdecl::Expose::Protocol (
3814 fdecl::ExposeProtocol {
3815 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3816 name: "logger".to_string(),
3817 collection: None,
3818 })),
3819 source_name: Some("fuchsia.logger.Log".to_string()),
3820 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3821 target_name: Some("fuchsia.logger.LegacyLog_same_as_target".to_string()),
3822 availability: Some(fdecl::Availability::SameAsTarget),
3823 ..Default::default()
3824 }
3825 ),
3826 fdecl::Expose::Protocol (
3827 fdecl::ExposeProtocol {
3828 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3829 name: "logger".to_string(),
3830 collection: None,
3831 })),
3832 source_name: Some("fuchsia.logger.Log".to_string()),
3833 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3834 target_name: Some("fuchsia.logger.LegacyLog_transitional".to_string()),
3835 availability: Some(fdecl::Availability::Transitional),
3836 ..Default::default()
3837 }
3838 ),
3839 ]),
3840 offers: None,
3841 capabilities: None,
3842 children: Some(vec![
3843 fdecl::Child {
3844 name: Some("logger".to_string()),
3845 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
3846 startup: Some(fdecl::StartupMode::Lazy),
3847 environment: None,
3848 on_terminate: None,
3849 ..Default::default()
3850 }
3851 ]),
3852 ..default_component_decl()
3853 },
3854 },
3855
3856 test_compile_expose_source_availability_unknown => {
3857 input = json!({
3858 "expose": [
3859 {
3860 "protocol": "fuchsia.logger.Log",
3861 "from": "#non-existent",
3862 "as": "fuchsia.logger.LegacyLog_non_existent",
3863 "availability": "optional",
3864 "source_availability": "unknown"
3865 },
3866 {
3867 "protocol": "fuchsia.logger.Log",
3868 "from": "#non-existent/dict",
3869 "as": "fuchsia.logger.LegacyLog_non_existent2",
3870 "availability": "optional",
3871 "source_availability": "unknown"
3872 },
3873 {
3874 "protocol": "fuchsia.logger.Log",
3875 "from": "#logger",
3876 "as": "fuchsia.logger.LegacyLog_child_exist",
3877 "availability": "optional",
3878 "source_availability": "unknown"
3879 },
3880 ],
3881 "children": [
3882 {
3883 "name": "logger",
3884 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
3885 },
3886 ],
3887 }),
3888 output = fdecl::Component {
3889 exposes: Some(vec![
3890 fdecl::Expose::Protocol (
3891 fdecl::ExposeProtocol {
3892 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
3893 source_name: Some("fuchsia.logger.Log".to_string()),
3894 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3895 target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
3896 availability: Some(fdecl::Availability::Optional),
3897 ..Default::default()
3898 }
3899 ),
3900 fdecl::Expose::Protocol (
3901 fdecl::ExposeProtocol {
3902 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
3903 source_name: Some("fuchsia.logger.Log".to_string()),
3904 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3905 target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
3906 availability: Some(fdecl::Availability::Optional),
3907 ..Default::default()
3908 }
3909 ),
3910 fdecl::Expose::Protocol (
3911 fdecl::ExposeProtocol {
3912 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
3913 name: "logger".to_string(),
3914 collection: None,
3915 })),
3916 source_name: Some("fuchsia.logger.Log".to_string()),
3917 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
3918 target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
3919 availability: Some(fdecl::Availability::Optional),
3920 ..Default::default()
3921 }
3922 ),
3923 ]),
3924 children: Some(vec![
3925 fdecl::Child {
3926 name: Some("logger".to_string()),
3927 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
3928 startup: Some(fdecl::StartupMode::Lazy),
3929 ..Default::default()
3930 }
3931 ]),
3932 ..default_component_decl()
3933 },
3934 },
3935
3936 test_compile_offer_source_availability_unknown => {
3937 input = json!({
3938 "offer": [
3939 {
3940 "protocol": "fuchsia.logger.Log",
3941 "from": "#non-existent",
3942 "as": "fuchsia.logger.LegacyLog_non_existent",
3943 "to": "#target",
3944 "availability": "optional",
3945 "source_availability": "unknown"
3946 },
3947 {
3948 "protocol": "fuchsia.logger.Log",
3949 "from": "#non-existent/dict",
3950 "as": "fuchsia.logger.LegacyLog_non_existent2",
3951 "to": "#target",
3952 "availability": "optional",
3953 "source_availability": "unknown"
3954 },
3955 {
3956 "protocol": "fuchsia.logger.Log",
3957 "from": "#logger",
3958 "as": "fuchsia.logger.LegacyLog_child_exist",
3959 "to": "#target",
3960 "availability": "optional",
3961 "source_availability": "unknown"
3962 },
3963 ],
3964 "children": [
3965 {
3966 "name": "logger",
3967 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
3968 },
3969 {
3970 "name": "target",
3971 "url": "#meta/target.cm"
3972 },
3973 ],
3974 }),
3975 output = fdecl::Component {
3976 offers: Some(vec![
3977 fdecl::Offer::Protocol (
3978 fdecl::OfferProtocol {
3979 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
3980 source_name: Some("fuchsia.logger.Log".to_string()),
3981 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
3982 name: "target".to_string(),
3983 collection: None,
3984 })),
3985 target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
3986 dependency_type: Some(fdecl::DependencyType::Strong),
3987 availability: Some(fdecl::Availability::Optional),
3988 ..Default::default()
3989 }
3990 ),
3991 fdecl::Offer::Protocol (
3992 fdecl::OfferProtocol {
3993 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
3994 source_name: Some("fuchsia.logger.Log".to_string()),
3995 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
3996 name: "target".to_string(),
3997 collection: None,
3998 })),
3999 target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
4000 dependency_type: Some(fdecl::DependencyType::Strong),
4001 availability: Some(fdecl::Availability::Optional),
4002 ..Default::default()
4003 }
4004 ),
4005 fdecl::Offer::Protocol (
4006 fdecl::OfferProtocol {
4007 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4008 name: "logger".to_string(),
4009 collection: None,
4010 })),
4011 source_name: Some("fuchsia.logger.Log".to_string()),
4012 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4013 name: "target".to_string(),
4014 collection: None,
4015 })),
4016 target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
4017 dependency_type: Some(fdecl::DependencyType::Strong),
4018 availability: Some(fdecl::Availability::Optional),
4019 ..Default::default()
4020 }
4021 ),
4022 ]),
4023 children: Some(vec![
4024 fdecl::Child {
4025 name: Some("logger".to_string()),
4026 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
4027 startup: Some(fdecl::StartupMode::Lazy),
4028 ..Default::default()
4029 },
4030 fdecl::Child {
4031 name: Some("target".to_string()),
4032 url: Some("#meta/target.cm".to_string()),
4033 startup: Some(fdecl::StartupMode::Lazy),
4034 ..Default::default()
4035 },
4036 ]),
4037 ..default_component_decl()
4038 },
4039 },
4040
4041 test_compile_offer => {
4042 input = json!({
4043 "offer": [
4044 {
4045 "protocol": "fuchsia.logger.LegacyLog",
4046 "from": "#logger",
4047 "to": "#netstack", "dependency": "weak"
4049 },
4050 {
4051 "protocol": "fuchsia.logger.LegacyLog",
4052 "from": "#logger",
4053 "to": [ "#modular" ], "as": "fuchsia.logger.LegacySysLog",
4055 "dependency": "strong"
4056 },
4057 {
4058 "protocol": [
4059 "fuchsia.setui.SetUiService",
4060 "fuchsia.test.service.Name"
4061 ],
4062 "from": "parent",
4063 "to": [ "#modular" ],
4064 "availability": "optional"
4065 },
4066 {
4067 "protocol": "fuchsia.sys2.StorageAdmin",
4068 "from": "#data",
4069 "to": [ "#modular" ],
4070 },
4071 {
4072 "protocol": "fuchsia.sys2.FromDict",
4073 "from": "parent/in/dict",
4074 "to": [ "#modular" ],
4075 },
4076 {
4077 "service": "svc",
4078 "from": [ "parent", "self", "#logger", "#modular" ],
4079 "to": "#netstack",
4080 },
4081 {
4082 "service": "fuchsia.sys2.FromDictService",
4083 "from": [ "parent/in/dict"],
4084 "to": "#modular",
4085 "dependency": "weak",
4086 },
4087 {
4088 "directory": "assets",
4089 "from": "parent",
4090 "to": [ "#netstack" ],
4091 "dependency": "weak",
4092 "availability": "same_as_target"
4093 },
4094 {
4095 "directory": [ "assets2", "assets3" ],
4096 "from": "parent",
4097 "to": [ "#modular", "#netstack" ],
4098 },
4099 {
4100 "directory": "data",
4101 "from": "parent",
4102 "to": [ "#modular" ],
4103 "as": "assets",
4104 "subdir": "index/file",
4105 "dependency": "strong"
4106 },
4107 {
4108 "directory": "hub",
4109 "from": "framework",
4110 "to": [ "#modular" ],
4111 "as": "hub",
4112 },
4113 {
4114 "storage": "data",
4115 "from": "self",
4116 "to": [
4117 "#netstack",
4118 "#modular"
4119 ],
4120 },
4121 {
4122 "storage": [ "storage_a", "storage_b" ],
4123 "from": "parent",
4124 "to": "#netstack",
4125 },
4126 {
4127 "runner": "elf",
4128 "from": "parent",
4129 "to": [ "#modular" ],
4130 "as": "elf-renamed",
4131 },
4132 {
4133 "runner": [ "runner_a", "runner_b" ],
4134 "from": "parent",
4135 "to": "#netstack",
4136 },
4137 {
4138 "resolver": "my_resolver",
4139 "from": "parent",
4140 "to": [ "#modular" ],
4141 "as": "pkg_resolver",
4142 },
4143 {
4144 "resolver": [ "resolver_a", "resolver_b" ],
4145 "from": "parent",
4146 "to": "#netstack",
4147 },
4148 {
4149 "dictionary": [ "dictionary_a", "dictionary_b" ],
4150 "from": "parent",
4151 "to": "#netstack",
4152 },
4153 {
4154 "event_stream": [
4155 "running",
4156 "started",
4157 ],
4158 "from": "parent",
4159 "to": "#netstack",
4160 },
4161 {
4162 "event_stream": "stopped",
4163 "from": "parent",
4164 "to": "#netstack",
4165 "as": "some_other_event",
4166 },
4167 ],
4168 "children": [
4169 {
4170 "name": "logger",
4171 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
4172 },
4173 {
4174 "name": "netstack",
4175 "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm"
4176 },
4177 ],
4178 "collections": [
4179 {
4180 "name": "modular",
4181 "durability": "transient",
4182 },
4183 ],
4184 "capabilities": [
4185 {
4186 "service": "svc",
4187 },
4188 {
4189 "storage": "data",
4190 "backing_dir": "minfs",
4191 "from": "#logger",
4192 "storage_id": "static_instance_id_or_moniker",
4193 },
4194 ],
4195 }),
4196 output = fdecl::Component {
4197 offers: Some(vec![
4198 fdecl::Offer::Protocol (
4199 fdecl::OfferProtocol {
4200 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4201 name: "logger".to_string(),
4202 collection: None,
4203 })),
4204 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
4205 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4206 name: "netstack".to_string(),
4207 collection: None,
4208 })),
4209 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
4210 dependency_type: Some(fdecl::DependencyType::Weak),
4211 availability: Some(fdecl::Availability::Required),
4212 ..Default::default()
4213 }
4214 ),
4215 fdecl::Offer::Protocol (
4216 fdecl::OfferProtocol {
4217 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4218 name: "logger".to_string(),
4219 collection: None,
4220 })),
4221 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
4222 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4223 name: "modular".to_string(),
4224 })),
4225 target_name: Some("fuchsia.logger.LegacySysLog".to_string()),
4226 dependency_type: Some(fdecl::DependencyType::Strong),
4227 availability: Some(fdecl::Availability::Required),
4228 ..Default::default()
4229 }
4230 ),
4231 fdecl::Offer::Protocol (
4232 fdecl::OfferProtocol {
4233 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4234 source_name: Some("fuchsia.setui.SetUiService".to_string()),
4235 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4236 name: "modular".to_string(),
4237 })),
4238 target_name: Some("fuchsia.setui.SetUiService".to_string()),
4239 dependency_type: Some(fdecl::DependencyType::Strong),
4240 availability: Some(fdecl::Availability::Optional),
4241 ..Default::default()
4242 }
4243 ),
4244 fdecl::Offer::Protocol (
4245 fdecl::OfferProtocol {
4246 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4247 source_name: Some("fuchsia.test.service.Name".to_string()),
4248 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4249 name: "modular".to_string(),
4250 })),
4251 target_name: Some("fuchsia.test.service.Name".to_string()),
4252 dependency_type: Some(fdecl::DependencyType::Strong),
4253 availability: Some(fdecl::Availability::Optional),
4254 ..Default::default()
4255 }
4256 ),
4257 fdecl::Offer::Protocol (
4258 fdecl::OfferProtocol {
4259 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4260 name: "data".to_string(),
4261 })),
4262 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4263 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4264 name: "modular".to_string(),
4265 })),
4266 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
4267 dependency_type: Some(fdecl::DependencyType::Strong),
4268 availability: Some(fdecl::Availability::Required),
4269 ..Default::default()
4270 }
4271 ),
4272 fdecl::Offer::Protocol (
4273 fdecl::OfferProtocol {
4274 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4275 #[cfg(fuchsia_api_level_at_least = "25")]
4276 source_dictionary: Some("in/dict".into()),
4277 source_name: Some("fuchsia.sys2.FromDict".to_string()),
4278 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4279 name: "modular".to_string(),
4280 })),
4281 target_name: Some("fuchsia.sys2.FromDict".to_string()),
4282 dependency_type: Some(fdecl::DependencyType::Strong),
4283 availability: Some(fdecl::Availability::Required),
4284 ..Default::default()
4285 }
4286 ),
4287 fdecl::Offer::Service (
4288 fdecl::OfferService {
4289 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4290 source_name: Some("svc".into()),
4291 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4292 name: "netstack".into(),
4293 collection: None,
4294 })),
4295 target_name: Some("svc".into()),
4296 availability: Some(fdecl::Availability::Required),
4297 dependency_type: Some(fdecl::DependencyType::Strong),
4298 ..Default::default()
4299 }
4300 ),
4301 fdecl::Offer::Service (
4302 fdecl::OfferService {
4303 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4304 source_name: Some("svc".into()),
4305 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4306 name: "netstack".into(),
4307 collection: None,
4308 })),
4309 target_name: Some("svc".into()),
4310 availability: Some(fdecl::Availability::Required),
4311 dependency_type: Some(fdecl::DependencyType::Strong),
4312 ..Default::default()
4313 }
4314 ),
4315 fdecl::Offer::Service (
4316 fdecl::OfferService {
4317 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4318 name: "logger".into(),
4319 collection: None,
4320 })),
4321 source_name: Some("svc".into()),
4322 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4323 name: "netstack".into(),
4324 collection: None,
4325 })),
4326 target_name: Some("svc".into()),
4327 availability: Some(fdecl::Availability::Required),
4328 dependency_type: Some(fdecl::DependencyType::Strong),
4329 ..Default::default()
4330 }
4331 ),
4332 fdecl::Offer::Service (
4333 fdecl::OfferService {
4334 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4335 name: "modular".into(),
4336 })),
4337 source_name: Some("svc".into()),
4338 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4339 name: "netstack".into(),
4340 collection: None,
4341 })),
4342 target_name: Some("svc".into()),
4343 availability: Some(fdecl::Availability::Required),
4344 dependency_type: Some(fdecl::DependencyType::Strong),
4345 ..Default::default()
4346 }
4347 ),
4348 fdecl::Offer::Service (
4349 fdecl::OfferService {
4350 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4351 source_name: Some("fuchsia.sys2.FromDictService".into()),
4352 #[cfg(fuchsia_api_level_at_least = "25")]
4353 source_dictionary: Some("in/dict".into()),
4354 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4355 name: "modular".into(),
4356 })),
4357 target_name: Some("fuchsia.sys2.FromDictService".to_string()),
4358 availability: Some(fdecl::Availability::Required),
4359 dependency_type: Some(fdecl::DependencyType::Weak),
4360 ..Default::default()
4361 }
4362 ),
4363 fdecl::Offer::Directory (
4364 fdecl::OfferDirectory {
4365 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4366 source_name: Some("assets".to_string()),
4367 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4368 name: "netstack".to_string(),
4369 collection: None,
4370 })),
4371 target_name: Some("assets".to_string()),
4372 rights: None,
4373 subdir: None,
4374 dependency_type: Some(fdecl::DependencyType::Weak),
4375 availability: Some(fdecl::Availability::SameAsTarget),
4376 ..Default::default()
4377 }
4378 ),
4379 fdecl::Offer::Directory (
4380 fdecl::OfferDirectory {
4381 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4382 source_name: Some("assets2".to_string()),
4383 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4384 name: "modular".to_string(),
4385 })),
4386 target_name: Some("assets2".to_string()),
4387 rights: None,
4388 subdir: None,
4389 dependency_type: Some(fdecl::DependencyType::Strong),
4390 availability: Some(fdecl::Availability::Required),
4391 ..Default::default()
4392 }
4393 ),
4394 fdecl::Offer::Directory (
4395 fdecl::OfferDirectory {
4396 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4397 source_name: Some("assets3".to_string()),
4398 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4399 name: "modular".to_string(),
4400 })),
4401 target_name: Some("assets3".to_string()),
4402 rights: None,
4403 subdir: None,
4404 dependency_type: Some(fdecl::DependencyType::Strong),
4405 availability: Some(fdecl::Availability::Required),
4406 ..Default::default()
4407 }
4408 ),
4409 fdecl::Offer::Directory (
4410 fdecl::OfferDirectory {
4411 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4412 source_name: Some("assets2".to_string()),
4413 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4414 name: "netstack".to_string(),
4415 collection: None,
4416 })),
4417 target_name: Some("assets2".to_string()),
4418 rights: None,
4419 subdir: None,
4420 dependency_type: Some(fdecl::DependencyType::Strong),
4421 availability: Some(fdecl::Availability::Required),
4422 ..Default::default()
4423 }
4424 ),
4425 fdecl::Offer::Directory (
4426 fdecl::OfferDirectory {
4427 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4428 source_name: Some("assets3".to_string()),
4429 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4430 name: "netstack".to_string(),
4431 collection: None,
4432 })),
4433 target_name: Some("assets3".to_string()),
4434 rights: None,
4435 subdir: None,
4436 dependency_type: Some(fdecl::DependencyType::Strong),
4437 availability: Some(fdecl::Availability::Required),
4438 ..Default::default()
4439 }
4440 ),
4441 fdecl::Offer::Directory (
4442 fdecl::OfferDirectory {
4443 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4444 source_name: Some("data".to_string()),
4445 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4446 name: "modular".to_string(),
4447 })),
4448 target_name: Some("assets".to_string()),
4449 rights: None,
4450 subdir: Some("index/file".to_string()),
4451 dependency_type: Some(fdecl::DependencyType::Strong),
4452 availability: Some(fdecl::Availability::Required),
4453 ..Default::default()
4454 }
4455 ),
4456 fdecl::Offer::Directory (
4457 fdecl::OfferDirectory {
4458 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4459 source_name: Some("hub".to_string()),
4460 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4461 name: "modular".to_string(),
4462 })),
4463 target_name: Some("hub".to_string()),
4464 rights: None,
4465 subdir: None,
4466 dependency_type: Some(fdecl::DependencyType::Strong),
4467 availability: Some(fdecl::Availability::Required),
4468 ..Default::default()
4469 }
4470 ),
4471 fdecl::Offer::Storage (
4472 fdecl::OfferStorage {
4473 source_name: Some("data".to_string()),
4474 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4475 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4476 name: "netstack".to_string(),
4477 collection: None,
4478 })),
4479 target_name: Some("data".to_string()),
4480 availability: Some(fdecl::Availability::Required),
4481 ..Default::default()
4482 }
4483 ),
4484 fdecl::Offer::Storage (
4485 fdecl::OfferStorage {
4486 source_name: Some("data".to_string()),
4487 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
4488 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4489 name: "modular".to_string(),
4490 })),
4491 target_name: Some("data".to_string()),
4492 availability: Some(fdecl::Availability::Required),
4493 ..Default::default()
4494 }
4495 ),
4496 fdecl::Offer::Storage (
4497 fdecl::OfferStorage {
4498 source_name: Some("storage_a".to_string()),
4499 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4500 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4501 name: "netstack".to_string(),
4502 collection: None,
4503 })),
4504 target_name: Some("storage_a".to_string()),
4505 availability: Some(fdecl::Availability::Required),
4506 ..Default::default()
4507 }
4508 ),
4509 fdecl::Offer::Storage (
4510 fdecl::OfferStorage {
4511 source_name: Some("storage_b".to_string()),
4512 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4513 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4514 name: "netstack".to_string(),
4515 collection: None,
4516 })),
4517 target_name: Some("storage_b".to_string()),
4518 availability: Some(fdecl::Availability::Required),
4519 ..Default::default()
4520 }
4521 ),
4522 fdecl::Offer::Runner (
4523 fdecl::OfferRunner {
4524 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4525 source_name: Some("elf".to_string()),
4526 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4527 name: "modular".to_string(),
4528 })),
4529 target_name: Some("elf-renamed".to_string()),
4530 ..Default::default()
4531 }
4532 ),
4533 fdecl::Offer::Runner (
4534 fdecl::OfferRunner {
4535 source_name: Some("runner_a".to_string()),
4536 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4537 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4538 name: "netstack".to_string(),
4539 collection: None,
4540 })),
4541 target_name: Some("runner_a".to_string()),
4542 ..Default::default()
4543 }
4544 ),
4545 fdecl::Offer::Runner (
4546 fdecl::OfferRunner {
4547 source_name: Some("runner_b".to_string()),
4548 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4549 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4550 name: "netstack".to_string(),
4551 collection: None,
4552 })),
4553 target_name: Some("runner_b".to_string()),
4554 ..Default::default()
4555 }
4556 ),
4557 fdecl::Offer::Resolver (
4558 fdecl::OfferResolver {
4559 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4560 source_name: Some("my_resolver".to_string()),
4561 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4562 name: "modular".to_string(),
4563 })),
4564 target_name: Some("pkg_resolver".to_string()),
4565 ..Default::default()
4566 }
4567 ),
4568 fdecl::Offer::Resolver (
4569 fdecl::OfferResolver {
4570 source_name: Some("resolver_a".to_string()),
4571 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4572 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4573 name: "netstack".to_string(),
4574 collection: None,
4575 })),
4576 target_name: Some("resolver_a".to_string()),
4577 ..Default::default()
4578 }
4579 ),
4580 fdecl::Offer::Resolver (
4581 fdecl::OfferResolver {
4582 source_name: Some("resolver_b".to_string()),
4583 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4584 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4585 name: "netstack".to_string(),
4586 collection: None,
4587 })),
4588 target_name: Some("resolver_b".to_string()),
4589 ..Default::default()
4590 }
4591 ),
4592 fdecl::Offer::Dictionary (
4593 fdecl::OfferDictionary {
4594 source_name: Some("dictionary_a".to_string()),
4595 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4596 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4597 name: "netstack".to_string(),
4598 collection: None,
4599 })),
4600 target_name: Some("dictionary_a".to_string()),
4601 dependency_type: Some(fdecl::DependencyType::Strong),
4602 availability: Some(fdecl::Availability::Required),
4603 ..Default::default()
4604 }
4605 ),
4606 fdecl::Offer::Dictionary (
4607 fdecl::OfferDictionary {
4608 source_name: Some("dictionary_b".to_string()),
4609 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4610 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4611 name: "netstack".to_string(),
4612 collection: None,
4613 })),
4614 target_name: Some("dictionary_b".to_string()),
4615 dependency_type: Some(fdecl::DependencyType::Strong),
4616 availability: Some(fdecl::Availability::Required),
4617 ..Default::default()
4618 }
4619 ),
4620 fdecl::Offer::EventStream (
4621 fdecl::OfferEventStream {
4622 source_name: Some("running".to_string()),
4623 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4624 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4625 name: "netstack".to_string(),
4626 collection: None,
4627 })),
4628 target_name: Some("running".to_string()),
4629 availability: Some(fdecl::Availability::Required),
4630 ..Default::default()
4631 }
4632 ),
4633 fdecl::Offer::EventStream (
4634 fdecl::OfferEventStream {
4635 source_name: Some("started".to_string()),
4636 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4637 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4638 name: "netstack".to_string(),
4639 collection: None,
4640 })),
4641 target_name: Some("started".to_string()),
4642 availability: Some(fdecl::Availability::Required),
4643 ..Default::default()
4644 }
4645 ),
4646 fdecl::Offer::EventStream (
4647 fdecl::OfferEventStream {
4648 source_name: Some("stopped".to_string()),
4649 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4650 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4651 name: "netstack".to_string(),
4652 collection: None,
4653 })),
4654 target_name: Some("some_other_event".to_string()),
4655 availability: Some(fdecl::Availability::Required),
4656 ..Default::default()
4657 }
4658 ),
4659 ]),
4660 capabilities: Some(vec![
4661 fdecl::Capability::Service (
4662 fdecl::Service {
4663 name: Some("svc".into()),
4664 source_path: Some("/svc/svc".into()),
4665 ..Default::default()
4666 },
4667 ),
4668 fdecl::Capability::Storage (
4669 fdecl::Storage {
4670 name: Some("data".to_string()),
4671 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4672 name: "logger".to_string(),
4673 collection: None,
4674 })),
4675 backing_dir: Some("minfs".to_string()),
4676 subdir: None,
4677 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
4678 ..Default::default()
4679 }
4680 )
4681 ]),
4682 children: Some(vec![
4683 fdecl::Child {
4684 name: Some("logger".to_string()),
4685 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
4686 startup: Some(fdecl::StartupMode::Lazy),
4687 environment: None,
4688 on_terminate: None,
4689 ..Default::default()
4690 },
4691 fdecl::Child {
4692 name: Some("netstack".to_string()),
4693 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
4694 startup: Some(fdecl::StartupMode::Lazy),
4695 environment: None,
4696 on_terminate: None,
4697 ..Default::default()
4698 },
4699 ]),
4700 collections: Some(vec![
4701 fdecl::Collection {
4702 name: Some("modular".to_string()),
4703 durability: Some(fdecl::Durability::Transient),
4704 environment: None,
4705 allowed_offers: None,
4706 ..Default::default()
4707 }
4708 ]),
4709 ..default_component_decl()
4710 },
4711 },
4712
4713 test_compile_offer_route_to_dictionary => {
4714 input = json!({
4715 "offer": [
4716 {
4717 "protocol": "A",
4718 "from": "parent/dict/1",
4719 "to": "self/dict",
4720 },
4721 {
4722 "runner": "B",
4723 "from": "#child",
4724 "to": "self/dict",
4725 },
4726 {
4727 "config": "B",
4728 "from": "parent/dict/2",
4729 "to": "self/dict",
4730 "as": "C",
4731 },
4732 ],
4733 "children": [
4734 {
4735 "name": "child",
4736 "url": "fuchsia-pkg://child"
4737 },
4738 ],
4739 "capabilities": [
4740 {
4741 "dictionary": "dict",
4742 },
4743 ],
4744 }),
4745 output = fdecl::Component {
4746 offers: Some(vec![
4747 fdecl::Offer::Protocol (
4748 fdecl::OfferProtocol {
4749 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4750 #[cfg(fuchsia_api_level_at_least = "25")]
4751 source_dictionary: Some("dict/1".into()),
4752 source_name: Some("A".into()),
4753 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4754 name: "dict".to_string(),
4755 })),
4756 target_name: Some("A".into()),
4757 dependency_type: Some(fdecl::DependencyType::Strong),
4758 availability: Some(fdecl::Availability::Required),
4759 ..Default::default()
4760 }
4761 ),
4762 fdecl::Offer::Runner (
4763 fdecl::OfferRunner {
4764 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4765 name: "child".into(),
4766 collection: None,
4767 })),
4768 source_name: Some("B".into()),
4769 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4770 name: "dict".to_string(),
4771 })),
4772 target_name: Some("B".into()),
4773 ..Default::default()
4774 }
4775 ),
4776 fdecl::Offer::Config (
4777 fdecl::OfferConfiguration {
4778 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4779 #[cfg(fuchsia_api_level_at_least = "25")]
4780 source_dictionary: Some("dict/2".into()),
4781 source_name: Some("B".into()),
4782 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
4783 name: "dict".to_string(),
4784 })),
4785 target_name: Some("C".into()),
4786 availability: Some(fdecl::Availability::Required),
4787 ..Default::default()
4788 }
4789 ),
4790 ]),
4791 capabilities: Some(vec![
4792 fdecl::Capability::Dictionary (
4793 fdecl::Dictionary {
4794 name: Some("dict".into()),
4795 ..Default::default()
4796 }
4797 )
4798 ]),
4799 children: Some(vec![
4800 fdecl::Child {
4801 name: Some("child".to_string()),
4802 url: Some("fuchsia-pkg://child".to_string()),
4803 startup: Some(fdecl::StartupMode::Lazy),
4804 ..Default::default()
4805 },
4806 ]),
4807 ..default_component_decl()
4808 },
4809 },
4810
4811
4812 test_compile_children => {
4813 input = json!({
4814 "children": [
4815 {
4816 "name": "logger",
4817 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4818 },
4819 {
4820 "name": "gmail",
4821 "url": "https://www.google.com/gmail",
4822 "startup": "eager",
4823 },
4824 {
4825 "name": "echo",
4826 "url": "fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm",
4827 "startup": "lazy",
4828 "on_terminate": "reboot",
4829 "environment": "#myenv",
4830 },
4831 ],
4832 "environments": [
4833 {
4834 "name": "myenv",
4835 "extends": "realm",
4836 },
4837 ],
4838 }),
4839 output = fdecl::Component {
4840 children: Some(vec![
4841 fdecl::Child {
4842 name: Some("logger".to_string()),
4843 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
4844 startup: Some(fdecl::StartupMode::Lazy),
4845 environment: None,
4846 on_terminate: None,
4847 ..Default::default()
4848 },
4849 fdecl::Child {
4850 name: Some("gmail".to_string()),
4851 url: Some("https://www.google.com/gmail".to_string()),
4852 startup: Some(fdecl::StartupMode::Eager),
4853 environment: None,
4854 on_terminate: None,
4855 ..Default::default()
4856 },
4857 fdecl::Child {
4858 name: Some("echo".to_string()),
4859 url: Some("fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm".to_string()),
4860 startup: Some(fdecl::StartupMode::Lazy),
4861 environment: Some("myenv".to_string()),
4862 on_terminate: Some(fdecl::OnTerminate::Reboot),
4863 ..Default::default()
4864 }
4865 ]),
4866 environments: Some(vec![
4867 fdecl::Environment {
4868 name: Some("myenv".to_string()),
4869 extends: Some(fdecl::EnvironmentExtends::Realm),
4870 runners: None,
4871 resolvers: None,
4872 stop_timeout_ms: None,
4873 ..Default::default()
4874 }
4875 ]),
4876 ..default_component_decl()
4877 },
4878 },
4879
4880 test_compile_collections => {
4881 input = json!({
4882 "collections": [
4883 {
4884 "name": "modular",
4885 "durability": "single_run",
4886 },
4887 {
4888 "name": "tests",
4889 "durability": "transient",
4890 "environment": "#myenv",
4891 },
4892 ],
4893 "environments": [
4894 {
4895 "name": "myenv",
4896 "extends": "realm",
4897 }
4898 ],
4899 }),
4900 output = fdecl::Component {
4901 collections: Some(vec![
4902 fdecl::Collection {
4903 name: Some("modular".to_string()),
4904 durability: Some(fdecl::Durability::SingleRun),
4905 environment: None,
4906 allowed_offers: None,
4907 ..Default::default()
4908 },
4909 fdecl::Collection {
4910 name: Some("tests".to_string()),
4911 durability: Some(fdecl::Durability::Transient),
4912 environment: Some("myenv".to_string()),
4913 allowed_offers: None,
4914 ..Default::default()
4915 }
4916 ]),
4917 environments: Some(vec![
4918 fdecl::Environment {
4919 name: Some("myenv".to_string()),
4920 extends: Some(fdecl::EnvironmentExtends::Realm),
4921 runners: None,
4922 resolvers: None,
4923 stop_timeout_ms: None,
4924 ..Default::default()
4925 }
4926 ]),
4927 ..default_component_decl()
4928 },
4929 },
4930
4931 test_compile_capabilities => {
4932 features = FeatureSet::from(vec![Feature::DynamicDictionaries]),
4933 input = json!({
4934 "capabilities": [
4935 {
4936 "protocol": "myprotocol",
4937 "path": "/protocol",
4938 },
4939 {
4940 "protocol": "myprotocol2",
4941 },
4942 {
4943 "protocol": [ "myprotocol3", "myprotocol4" ],
4944 },
4945 {
4946 "directory": "mydirectory",
4947 "path": "/directory",
4948 "rights": [ "connect" ],
4949 },
4950 {
4951 "storage": "mystorage",
4952 "backing_dir": "storage",
4953 "from": "#minfs",
4954 "storage_id": "static_instance_id_or_moniker",
4955 },
4956 {
4957 "storage": "mystorage2",
4958 "backing_dir": "storage2",
4959 "from": "#minfs",
4960 "storage_id": "static_instance_id",
4961 },
4962 {
4963 "runner": "myrunner",
4964 "path": "/runner",
4965 },
4966 {
4967 "resolver": "myresolver",
4968 "path": "/resolver"
4969 },
4970 {
4971 "dictionary": "dict1",
4972 },
4973 {
4974 "dictionary": "dict2",
4975 "path": "/in/a",
4976 },
4977 ],
4978 "children": [
4979 {
4980 "name": "minfs",
4981 "url": "fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm",
4982 },
4983 ]
4984 }),
4985 output = fdecl::Component {
4986 capabilities: Some(vec![
4987 fdecl::Capability::Protocol (
4988 fdecl::Protocol {
4989 name: Some("myprotocol".to_string()),
4990 source_path: Some("/protocol".to_string()),
4991 ..Default::default()
4992 }
4993 ),
4994 fdecl::Capability::Protocol (
4995 fdecl::Protocol {
4996 name: Some("myprotocol2".to_string()),
4997 source_path: Some("/svc/myprotocol2".to_string()),
4998 ..Default::default()
4999 }
5000 ),
5001 fdecl::Capability::Protocol (
5002 fdecl::Protocol {
5003 name: Some("myprotocol3".to_string()),
5004 source_path: Some("/svc/myprotocol3".to_string()),
5005 ..Default::default()
5006 }
5007 ),
5008 fdecl::Capability::Protocol (
5009 fdecl::Protocol {
5010 name: Some("myprotocol4".to_string()),
5011 source_path: Some("/svc/myprotocol4".to_string()),
5012 ..Default::default()
5013 }
5014 ),
5015 fdecl::Capability::Directory (
5016 fdecl::Directory {
5017 name: Some("mydirectory".to_string()),
5018 source_path: Some("/directory".to_string()),
5019 rights: Some(fio::Operations::CONNECT),
5020 ..Default::default()
5021 }
5022 ),
5023 fdecl::Capability::Storage (
5024 fdecl::Storage {
5025 name: Some("mystorage".to_string()),
5026 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5027 name: "minfs".to_string(),
5028 collection: None,
5029 })),
5030 backing_dir: Some("storage".to_string()),
5031 subdir: None,
5032 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
5033 ..Default::default()
5034 }
5035 ),
5036 fdecl::Capability::Storage (
5037 fdecl::Storage {
5038 name: Some("mystorage2".to_string()),
5039 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5040 name: "minfs".to_string(),
5041 collection: None,
5042 })),
5043 backing_dir: Some("storage2".to_string()),
5044 subdir: None,
5045 storage_id: Some(fdecl::StorageId::StaticInstanceId),
5046 ..Default::default()
5047 }
5048 ),
5049 fdecl::Capability::Runner (
5050 fdecl::Runner {
5051 name: Some("myrunner".to_string()),
5052 source_path: Some("/runner".to_string()),
5053 ..Default::default()
5054 }
5055 ),
5056 fdecl::Capability::Resolver (
5057 fdecl::Resolver {
5058 name: Some("myresolver".to_string()),
5059 source_path: Some("/resolver".to_string()),
5060 ..Default::default()
5061 }
5062 ),
5063 fdecl::Capability::Dictionary (
5064 fdecl::Dictionary {
5065 name: Some("dict1".into()),
5066 ..Default::default()
5067 }
5068 ),
5069 fdecl::Capability::Dictionary (
5070 fdecl::Dictionary {
5071 name: Some("dict2".into()),
5072 source: None,
5073 source_dictionary: None,
5074 source_path: Some("/in/a".into()),
5075 ..Default::default()
5076 }
5077 ),
5078 ]),
5079 children: Some(vec![
5080 fdecl::Child {
5081 name: Some("minfs".to_string()),
5082 url: Some("fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm".to_string()),
5083 startup: Some(fdecl::StartupMode::Lazy),
5084 environment: None,
5085 on_terminate: None,
5086 ..Default::default()
5087 }
5088 ]),
5089 ..default_component_decl()
5090 },
5091 },
5092
5093 test_compile_facets => {
5094 input = json!({
5095 "facets": {
5096 "title": "foo",
5097 "authors": [ "me", "you" ],
5098 "year": "2018",
5099 "metadata": {
5100 "publisher": "The Books Publisher",
5101 }
5102 }
5103 }),
5104 output = fdecl::Component {
5105 facets: Some(fdata::Dictionary {
5106 entries: Some(vec![
5107 fdata::DictionaryEntry {
5108 key: "authors".to_string(),
5109 value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["me".to_owned(), "you".to_owned()]))),
5110 },
5111 fdata::DictionaryEntry {
5112 key: "metadata.publisher".to_string(),
5113 value: Some(Box::new(fdata::DictionaryValue::Str("The Books Publisher".to_string()))),
5114 },
5115 fdata::DictionaryEntry {
5116 key: "title".to_string(),
5117 value: Some(Box::new(fdata::DictionaryValue::Str("foo".to_string()))),
5118 },
5119 fdata::DictionaryEntry {
5120 key: "year".to_string(),
5121 value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
5122 },
5123 ]),
5124 ..Default::default()
5125 }
5126 ),
5127 ..default_component_decl()
5128 },
5129 },
5130
5131 test_compile_environment => {
5132 input = json!({
5133 "environments": [
5134 {
5135 "name": "myenv",
5136 "__stop_timeout_ms": 10u32,
5137 },
5138 {
5139 "name": "myenv2",
5140 "extends": "realm",
5141 },
5142 {
5143 "name": "myenv3",
5144 "extends": "none",
5145 "__stop_timeout_ms": 8000u32,
5146 }
5147 ],
5148 }),
5149 output = fdecl::Component {
5150 environments: Some(vec![
5151 fdecl::Environment {
5152 name: Some("myenv".to_string()),
5153 extends: Some(fdecl::EnvironmentExtends::None),
5154 runners: None,
5155 resolvers: None,
5156 stop_timeout_ms: Some(10),
5157 ..Default::default()
5158 },
5159 fdecl::Environment {
5160 name: Some("myenv2".to_string()),
5161 extends: Some(fdecl::EnvironmentExtends::Realm),
5162 runners: None,
5163 resolvers: None,
5164 stop_timeout_ms: None,
5165 ..Default::default()
5166 },
5167 fdecl::Environment {
5168 name: Some("myenv3".to_string()),
5169 extends: Some(fdecl::EnvironmentExtends::None),
5170 runners: None,
5171 resolvers: None,
5172 stop_timeout_ms: Some(8000),
5173 ..Default::default()
5174 },
5175 ]),
5176 ..default_component_decl()
5177 },
5178 },
5179
5180 test_compile_environment_with_runner_and_resolver => {
5181 input = json!({
5182 "environments": [
5183 {
5184 "name": "myenv",
5185 "extends": "realm",
5186 "runners": [
5187 {
5188 "runner": "dart",
5189 "from": "parent",
5190 }
5191 ],
5192 "resolvers": [
5193 {
5194 "resolver": "pkg_resolver",
5195 "from": "parent",
5196 "scheme": "fuchsia-pkg",
5197 }
5198 ],
5199 },
5200 ],
5201 }),
5202 output = fdecl::Component {
5203 environments: Some(vec![
5204 fdecl::Environment {
5205 name: Some("myenv".to_string()),
5206 extends: Some(fdecl::EnvironmentExtends::Realm),
5207 runners: Some(vec![
5208 fdecl::RunnerRegistration {
5209 source_name: Some("dart".to_string()),
5210 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5211 target_name: Some("dart".to_string()),
5212 ..Default::default()
5213 }
5214 ]),
5215 resolvers: Some(vec![
5216 fdecl::ResolverRegistration {
5217 resolver: Some("pkg_resolver".to_string()),
5218 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5219 scheme: Some("fuchsia-pkg".to_string()),
5220 ..Default::default()
5221 }
5222 ]),
5223 stop_timeout_ms: None,
5224 ..Default::default()
5225 },
5226 ]),
5227 ..default_component_decl()
5228 },
5229 },
5230
5231 test_compile_environment_with_runner_alias => {
5232 input = json!({
5233 "environments": [
5234 {
5235 "name": "myenv",
5236 "extends": "realm",
5237 "runners": [
5238 {
5239 "runner": "dart",
5240 "from": "parent",
5241 "as": "my-dart",
5242 }
5243 ],
5244 },
5245 ],
5246 }),
5247 output = fdecl::Component {
5248 environments: Some(vec![
5249 fdecl::Environment {
5250 name: Some("myenv".to_string()),
5251 extends: Some(fdecl::EnvironmentExtends::Realm),
5252 runners: Some(vec![
5253 fdecl::RunnerRegistration {
5254 source_name: Some("dart".to_string()),
5255 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5256 target_name: Some("my-dart".to_string()),
5257 ..Default::default()
5258 }
5259 ]),
5260 resolvers: None,
5261 stop_timeout_ms: None,
5262 ..Default::default()
5263 },
5264 ]),
5265 ..default_component_decl()
5266 },
5267 },
5268
5269 test_compile_environment_with_debug => {
5270 input = json!({
5271 "capabilities": [
5272 {
5273 "protocol": "fuchsia.serve.service",
5274 },
5275 ],
5276 "environments": [
5277 {
5278 "name": "myenv",
5279 "extends": "realm",
5280 "debug": [
5281 {
5282 "protocol": "fuchsia.serve.service",
5283 "from": "self",
5284 "as": "my-service",
5285 }
5286 ],
5287 },
5288 ],
5289 }),
5290 output = fdecl::Component {
5291 capabilities: Some(vec![
5292 fdecl::Capability::Protocol(
5293 fdecl::Protocol {
5294 name : Some("fuchsia.serve.service".to_owned()),
5295 source_path: Some("/svc/fuchsia.serve.service".to_owned()),
5296 ..Default::default()
5297 }
5298 )
5299 ]),
5300 environments: Some(vec![
5301 fdecl::Environment {
5302 name: Some("myenv".to_string()),
5303 extends: Some(fdecl::EnvironmentExtends::Realm),
5304 debug_capabilities: Some(vec![
5305 fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
5306 source_name: Some("fuchsia.serve.service".to_string()),
5307 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5308 target_name: Some("my-service".to_string()),
5309 ..Default::default()
5310 }),
5311 ]),
5312 resolvers: None,
5313 runners: None,
5314 stop_timeout_ms: None,
5315 ..Default::default()
5316 },
5317 ]),
5318 ..default_component_decl()
5319 },
5320 },
5321
5322
5323 test_compile_configuration_capability => {
5324 input = json!({
5325 "capabilities": [
5326 {
5327 "config": "fuchsia.config.true",
5328 "type": "bool",
5329 "value": true,
5330 },
5331 {
5332 "config": "fuchsia.config.false",
5333 "type": "bool",
5334 "value": false,
5335 },
5336 ],
5337 }),
5338 output = fdecl::Component {
5339 capabilities: Some(vec![
5340 fdecl::Capability::Config (
5341 fdecl::Configuration {
5342 name: Some("fuchsia.config.true".to_string()),
5343 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
5344 ..Default::default()
5345 }),
5346 fdecl::Capability::Config (
5347 fdecl::Configuration {
5348 name: Some("fuchsia.config.false".to_string()),
5349 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(false))),
5350 ..Default::default()
5351 }),
5352 ]),
5353 ..default_component_decl()
5354 },
5355 },
5356
5357 test_compile_all_sections => {
5358 input = json!({
5359 "program": {
5360 "runner": "elf",
5361 "binary": "bin/app",
5362 },
5363 "use": [
5364 { "protocol": "LegacyCoolFonts", "path": "/svc/fuchsia.fonts.LegacyProvider" },
5365 { "protocol": [ "ReallyGoodFonts", "IWouldNeverUseTheseFonts"]},
5366 { "protocol": "DebugProtocol", "from": "debug"},
5367 ],
5368 "expose": [
5369 { "directory": "blobfs", "from": "self", "rights": ["r*"]},
5370 ],
5371 "offer": [
5372 {
5373 "protocol": "fuchsia.logger.LegacyLog",
5374 "from": "#logger",
5375 "to": [ "#netstack", "#modular" ],
5376 "dependency": "weak"
5377 },
5378 ],
5379 "children": [
5380 {
5381 "name": "logger",
5382 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
5383 },
5384 {
5385 "name": "netstack",
5386 "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm",
5387 },
5388 ],
5389 "collections": [
5390 {
5391 "name": "modular",
5392 "durability": "transient",
5393 },
5394 ],
5395 "capabilities": [
5396 {
5397 "directory": "blobfs",
5398 "path": "/volumes/blobfs",
5399 "rights": [ "r*" ],
5400 },
5401 {
5402 "runner": "myrunner",
5403 "path": "/runner",
5404 },
5405 {
5406 "protocol": "fuchsia.serve.service",
5407 }
5408 ],
5409 "facets": {
5410 "author": "Fuchsia",
5411 "year": "2018",
5412 },
5413 "environments": [
5414 {
5415 "name": "myenv",
5416 "extends": "realm",
5417 "debug": [
5418 {
5419 "protocol": "fuchsia.serve.service",
5420 "from": "self",
5421 "as": "my-service",
5422 },
5423 {
5424 "protocol": "fuchsia.logger.LegacyLog",
5425 "from": "#logger",
5426 }
5427 ]
5428 }
5429 ],
5430 }),
5431 output = fdecl::Component {
5432 program: Some(fdecl::Program {
5433 runner: Some("elf".to_string()),
5434 info: Some(fdata::Dictionary {
5435 entries: Some(vec![fdata::DictionaryEntry {
5436 key: "binary".to_string(),
5437 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5438 }]),
5439 ..Default::default()
5440 }),
5441 ..Default::default()
5442 }),
5443 uses: Some(vec![
5444 fdecl::Use::Protocol (
5445 fdecl::UseProtocol {
5446 dependency_type: Some(fdecl::DependencyType::Strong),
5447 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5448 source_name: Some("LegacyCoolFonts".to_string()),
5449 target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
5450 availability: Some(fdecl::Availability::Required),
5451 ..Default::default()
5452 }
5453 ),
5454 fdecl::Use::Protocol (
5455 fdecl::UseProtocol {
5456 dependency_type: Some(fdecl::DependencyType::Strong),
5457 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5458 source_name: Some("ReallyGoodFonts".to_string()),
5459 target_path: Some("/svc/ReallyGoodFonts".to_string()),
5460 availability: Some(fdecl::Availability::Required),
5461 ..Default::default()
5462 }
5463 ),
5464 fdecl::Use::Protocol (
5465 fdecl::UseProtocol {
5466 dependency_type: Some(fdecl::DependencyType::Strong),
5467 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5468 source_name: Some("IWouldNeverUseTheseFonts".to_string()),
5469 target_path: Some("/svc/IWouldNeverUseTheseFonts".to_string()),
5470 availability: Some(fdecl::Availability::Required),
5471 ..Default::default()
5472 }
5473 ),
5474 fdecl::Use::Protocol (
5475 fdecl::UseProtocol {
5476 dependency_type: Some(fdecl::DependencyType::Strong),
5477 source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
5478 source_name: Some("DebugProtocol".to_string()),
5479 target_path: Some("/svc/DebugProtocol".to_string()),
5480 availability: Some(fdecl::Availability::Required),
5481 ..Default::default()
5482 }
5483 ),
5484 ]),
5485 exposes: Some(vec![
5486 fdecl::Expose::Directory (
5487 fdecl::ExposeDirectory {
5488 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5489 source_name: Some("blobfs".to_string()),
5490 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5491 target_name: Some("blobfs".to_string()),
5492 rights: Some(
5493 fio::Operations::CONNECT | fio::Operations::ENUMERATE |
5494 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
5495 fio::Operations::GET_ATTRIBUTES
5496 ),
5497 subdir: None,
5498 availability: Some(fdecl::Availability::Required),
5499 ..Default::default()
5500 }
5501 ),
5502 ]),
5503 offers: Some(vec![
5504 fdecl::Offer::Protocol (
5505 fdecl::OfferProtocol {
5506 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5507 name: "logger".to_string(),
5508 collection: None,
5509 })),
5510 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5511 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
5512 name: "netstack".to_string(),
5513 collection: None,
5514 })),
5515 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5516 dependency_type: Some(fdecl::DependencyType::Weak),
5517 availability: Some(fdecl::Availability::Required),
5518 ..Default::default()
5519 }
5520 ),
5521 fdecl::Offer::Protocol (
5522 fdecl::OfferProtocol {
5523 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5524 name: "logger".to_string(),
5525 collection: None,
5526 })),
5527 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5528 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5529 name: "modular".to_string(),
5530 })),
5531 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5532 dependency_type: Some(fdecl::DependencyType::Weak),
5533 availability: Some(fdecl::Availability::Required),
5534 ..Default::default()
5535 }
5536 ),
5537 ]),
5538 capabilities: Some(vec![
5539 fdecl::Capability::Directory (
5540 fdecl::Directory {
5541 name: Some("blobfs".to_string()),
5542 source_path: Some("/volumes/blobfs".to_string()),
5543 rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
5544 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
5545 fio::Operations::GET_ATTRIBUTES
5546 ),
5547 ..Default::default()
5548 }
5549 ),
5550 fdecl::Capability::Runner (
5551 fdecl::Runner {
5552 name: Some("myrunner".to_string()),
5553 source_path: Some("/runner".to_string()),
5554 ..Default::default()
5555 }
5556 ),
5557 fdecl::Capability::Protocol(
5558 fdecl::Protocol {
5559 name : Some("fuchsia.serve.service".to_owned()),
5560 source_path: Some("/svc/fuchsia.serve.service".to_owned()),
5561 ..Default::default()
5562 }
5563 )
5564 ]),
5565 children: Some(vec![
5566 fdecl::Child {
5567 name: Some("logger".to_string()),
5568 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
5569 startup: Some(fdecl::StartupMode::Lazy),
5570 environment: None,
5571 on_terminate: None,
5572 ..Default::default()
5573 },
5574 fdecl::Child {
5575 name: Some("netstack".to_string()),
5576 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
5577 startup: Some(fdecl::StartupMode::Lazy),
5578 environment: None,
5579 on_terminate: None,
5580 ..Default::default()
5581 },
5582 ]),
5583 collections: Some(vec![
5584 fdecl::Collection {
5585 name: Some("modular".to_string()),
5586 durability: Some(fdecl::Durability::Transient),
5587 environment: None,
5588 allowed_offers: None,
5589 ..Default::default()
5590 }
5591 ]),
5592 environments: Some(vec![
5593 fdecl::Environment {
5594 name: Some("myenv".to_string()),
5595 extends: Some(fdecl::EnvironmentExtends::Realm),
5596 runners: None,
5597 resolvers: None,
5598 stop_timeout_ms: None,
5599 debug_capabilities: Some(vec![
5600 fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
5601 source_name: Some("fuchsia.serve.service".to_string()),
5602 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5603 target_name: Some("my-service".to_string()),
5604 ..Default::default()
5605 }),
5606 fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
5607 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
5608 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5609 name: "logger".to_string(),
5610 collection: None,
5611 })),
5612 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5613 ..Default::default()
5614 }),
5615 ]),
5616 ..Default::default()
5617 }
5618 ]),
5619 facets: Some(fdata::Dictionary {
5620 entries: Some(vec![
5621 fdata::DictionaryEntry {
5622 key: "author".to_string(),
5623 value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
5624 },
5625 fdata::DictionaryEntry {
5626 key: "year".to_string(),
5627 value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
5628 },
5629 ]),
5630 ..Default::default()
5631 }),
5632 ..Default::default()
5633 },
5634 },
5635 }
5636
5637 #[test]
5638 fn test_maybe_generate_specialization_from_all() {
5639 let offer = create_offer(
5640 "fuchsia.logger.LegacyLog",
5641 OneOrMany::One(OfferFromRef::Parent {}),
5642 OneOrMany::One(OfferToRef::All),
5643 );
5644
5645 let mut offer_set = vec![create_offer(
5646 "fuchsia.logger.LogSink",
5647 OneOrMany::One(OfferFromRef::Parent {}),
5648 OneOrMany::One(OfferToRef::All),
5649 )];
5650
5651 let result = maybe_generate_direct_offer_from_all(
5652 &offer,
5653 &offer_set,
5654 &Name::from_str("something").unwrap(),
5655 );
5656
5657 assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
5658 assert_eq!(
5659 protocol,
5660 &Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
5661 );
5662 assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
5663 assert_eq!(
5664 to,
5665 &OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
5666 );
5667 });
5668
5669 offer_set.push(create_offer(
5670 "fuchsia.inspect.InspectSink",
5671 OneOrMany::One(OfferFromRef::Parent {}),
5672 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
5673 ));
5674
5675 let result = maybe_generate_direct_offer_from_all(
5676 &offer,
5677 &offer_set,
5678 &Name::from_str("something").unwrap(),
5679 );
5680
5681 assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
5682 assert_eq!(
5683 protocol,
5684 &Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
5685 );
5686 assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
5687 assert_eq!(
5688 to,
5689 &OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
5690 );
5691 });
5692
5693 offer_set.push(create_offer(
5694 "fuchsia.logger.LegacyLog",
5695 OneOrMany::One(OfferFromRef::Parent {}),
5696 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
5697 ));
5698
5699 assert!(maybe_generate_direct_offer_from_all(
5700 &offer,
5701 &offer_set,
5702 &Name::from_str("something").unwrap()
5703 )
5704 .is_empty());
5705 }
5706
5707 #[test]
5708 fn test_expose_void_service_capability() {
5709 let input = must_parse_cml!({
5710 "expose": [
5711 {
5712 "service": "fuchsia.foo.Bar",
5713 "from": [ "#non_existent_child" ],
5714 "source_availability": "unknown",
5715 },
5716 ],
5717 });
5718 let result = compile(&input, CompileOptions::default());
5719 assert_matches!(result, Ok(_));
5720 }
5721
5722 #[test]
5724 fn test_aggregated_capabilities_must_use_same_availability_expose() {
5725 let input = must_parse_cml!({
5727 "expose": [
5728 {
5729 "service": "fuchsia.foo.Bar",
5730 "from": [ "#a", "#b" ],
5731 "availability": "optional",
5732 },
5733 ],
5734 "collections": [
5735 {
5736 "name": "a",
5737 "durability": "transient",
5738 },
5739 {
5740 "name": "b",
5741 "durability": "transient",
5742 },
5743 ],
5744 });
5745 let result = compile(&input, CompileOptions::default());
5746 assert_matches!(result, Ok(_));
5747
5748 let input = must_parse_cml!({
5750 "expose": [
5751 {
5752 "service": "fuchsia.foo.Bar",
5753 "from": [ "#a", "#non_existent" ],
5754 "source_availability": "unknown",
5755 },
5756 ],
5757 "collections": [
5758 {
5759 "name": "a",
5760 "durability": "transient",
5761 },
5762 ],
5763 });
5764 let result = compile(&input, CompileOptions::default());
5765 assert_matches!(
5766 result,
5767 Err(Error::FidlValidator { errs: ErrorList { errs } })
5768 if matches!(
5769 &errs[..],
5770 [
5771 CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
5772 ]
5773 if matches!(
5774 &availabilities[..],
5775 [ fdecl::Availability::Required, fdecl::Availability::Optional, ]
5776 )
5777 )
5778 );
5779 }
5780
5781 #[test]
5782 fn test_aggregated_capabilities_must_use_same_availability_offer() {
5783 let input = must_parse_cml!({
5785 "offer": [
5786 {
5787 "service": "fuchsia.foo.Bar",
5788 "from": [ "#a", "#b" ],
5789 "to": "#c",
5790 "availability": "optional",
5791 },
5792 ],
5793 "collections": [
5794 {
5795 "name": "a",
5796 "durability": "transient",
5797 },
5798 {
5799 "name": "b",
5800 "durability": "transient",
5801 },
5802 ],
5803 "children": [
5804 {
5805 "name": "c",
5806 "url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
5807 },
5808 ],
5809 });
5810 let result = compile(&input, CompileOptions::default());
5811 assert_matches!(result, Ok(_));
5812
5813 let input = must_parse_cml!({
5815 "offer": [
5816 {
5817 "service": "fuchsia.foo.Bar",
5818 "from": [ "#a", "#non_existent" ],
5819 "to": "#c",
5820 "source_availability": "unknown",
5821 },
5822 ],
5823 "collections": [
5824 {
5825 "name": "a",
5826 "durability": "transient",
5827 },
5828 ],
5829 "children": [
5830 {
5831 "name": "c",
5832 "url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
5833 },
5834 ],
5835 });
5836 let result = compile(&input, CompileOptions::default());
5837 assert_matches!(
5838 result,
5839 Err(Error::FidlValidator { errs: ErrorList { errs } })
5840 if matches!(
5841 &errs[..],
5842 [
5843 CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
5844 ]
5845 if matches!(
5846 &availabilities[..],
5847 [ fdecl::Availability::Required, fdecl::Availability::Optional, ]
5848 )
5849 )
5850 );
5851 }
5852
5853 #[test]
5854 fn test_compile_offer_to_all_exact_duplicate_disallowed() {
5855 let input = must_parse_cml!({
5856 "children": [
5857 {
5858 "name": "logger",
5859 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
5860 },
5861 ],
5862 "offer": [
5863 {
5864 "protocol": "fuchsia.logger.LogSink",
5865 "from": "parent",
5866 "to": "all",
5867 },
5868 {
5869 "protocol": "fuchsia.logger.LogSink",
5870 "from": "parent",
5871 "to": "all",
5872 },
5873 ],
5874 });
5875 assert_matches!(
5876 compile(&input, CompileOptions::default()),
5877 Err(Error::Validate { err, .. })
5878 if &err == "Protocol(s) [\"fuchsia.logger.LogSink\"] offered to \"all\" multiple times"
5879 );
5880 }
5881
5882 #[test]
5883 fn test_compile_use_config() {
5884 let input = must_parse_cml!({
5885 "use": [
5886 {
5887 "config": "fuchsia.config.Config",
5888 "key" : "my_config",
5889 "type": "bool",
5890 }
5891 ],
5892 });
5893 let options = CompileOptions::new().config_package_path("fake.cvf");
5894 let actual = compile(&input, options).unwrap();
5895 let type_ = fdecl::ConfigType {
5896 layout: fdecl::ConfigTypeLayout::Bool,
5897 parameters: Some(vec![]),
5898 constraints: vec![],
5899 };
5900 assert_eq!(
5901 actual.uses.unwrap(),
5902 vec![fdecl::Use::Config(fdecl::UseConfiguration {
5903 source_name: Some("fuchsia.config.Config".to_string()),
5904 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5905 target_name: Some("my_config".to_string()),
5906 availability: Some(fdecl::Availability::Required),
5907 type_: Some(type_.clone()),
5908 ..Default::default()
5909 })]
5910 );
5911 assert_eq!(
5912 actual.config.unwrap().fields.unwrap(),
5913 [fdecl::ConfigField {
5914 key: Some("my_config".to_string()),
5915 type_: Some(type_),
5916 mutability: Some(fdecl::ConfigMutability::default()),
5917 ..Default::default()
5918 }]
5919 .to_vec(),
5920 );
5921 }
5922
5923 #[test]
5924 fn test_compile_use_config_optional_bad_type() {
5925 let input = must_parse_cml!({
5926 "use": [
5927 {
5928 "config": "fuchsia.config.Config",
5929 "key" : "my_config",
5930 "type": "bool",
5931 "availability": "optional",
5932 }
5933 ],
5934 "config": {
5935 "my_config": { "type": "int8"},
5936 }
5937 });
5938 let options = CompileOptions::new().config_package_path("fake.cvf");
5939 assert_matches!(
5940 compile(&input, options),
5941 Err(Error::Validate { err, .. })
5942 if &err == "Use and config block differ on type for key 'my_config'"
5943 );
5944 }
5945
5946 #[test]
5947 fn test_config_source_from_package() {
5948 let input = must_parse_cml!({
5949 "use": [
5950 {
5951 "config": "fuchsia.config.Config",
5952 "key" : "my_config",
5953 "type": "bool",
5954 "availability": "optional",
5955 }
5956 ],
5957 "config": {
5958 "my_config": { "type": "bool"},
5959 }
5960 });
5961 let options = CompileOptions::new().config_package_path("fake.cvf");
5962 let decl = compile(&input, options).unwrap();
5963 let config = decl.config.unwrap();
5964 assert_eq!(
5965 config.value_source,
5966 Some(fdecl::ConfigValueSource::PackagePath("fake.cvf".into()))
5967 );
5968 }
5969
5970 #[test]
5971 fn test_config_source_from_capabilities() {
5972 let input = must_parse_cml!({
5973 "use": [
5974 {
5975 "config": "fuchsia.config.Config",
5976 "key" : "my_config",
5977 "type": "bool",
5978 }
5979 ],
5980 });
5981 let options = CompileOptions::new().config_package_path("fake.cvf");
5982 let decl = compile(&input, options).unwrap();
5983 let config = decl.config.unwrap();
5984 assert_eq!(
5985 config.value_source,
5986 Some(
5987 fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default())
5988 )
5989 );
5990 }
5991
5992 #[test]
5993 fn test_config_default() {
5994 let input = must_parse_cml!({
5995 "use": [
5996 {
5997 "config": "fuchsia.config.Config",
5998 "key" : "my_config",
5999 "type": "bool",
6000 "availability": "optional",
6001 "default": true
6002 }
6003 ],
6004 });
6005 let options = CompileOptions::new().config_package_path("fake.cvf");
6006 let decl = compile(&input, options).unwrap();
6007 assert_matches!(
6008 decl.uses.as_ref().unwrap()[0],
6009 fdecl::Use::Config(fdecl::UseConfiguration {
6010 default: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
6011 ..
6012 })
6013 );
6014 }
6015
6016 #[test]
6017 fn test_config_default_bad_type() {
6018 let input = must_parse_cml!({
6019 "use": [
6020 {
6021 "config": "fuchsia.config.Config",
6022 "key" : "my_config",
6023 "type": "bool",
6024 "availability": "optional",
6025 "default": 5
6026 }
6027 ],
6028 });
6029 let options = CompileOptions::new().config_package_path("fake.cvf");
6030 assert_matches!(compile(&input, options), Err(Error::InvalidArgs(_)));
6031 }
6032
6033 #[test]
6034 fn test_compile_protocol_delivery_type() {
6035 let input = must_parse_cml!({
6036 "capabilities": [
6037 {
6038 "protocol": "fuchsia.echo.Echo",
6039 "delivery": "on_readable",
6040 }
6041 ],
6042 });
6043 let features = FeatureSet::from(vec![Feature::DeliveryType]);
6044 let options = CompileOptions::new().features(&features);
6045 let decl = compile(&input, options).unwrap();
6046 assert_matches!(
6047 decl.capabilities.as_ref().unwrap()[0],
6048 fdecl::Capability::Protocol(fdecl::Protocol {
6049 delivery: Some(fdecl::DeliveryType::OnReadable),
6050 ..
6051 })
6052 );
6053 }
6054
6055 #[test]
6056 fn test_compile_protocol_setting_delivery_type_requires_feature_flag() {
6057 let input = must_parse_cml!({
6058 "capabilities": [
6059 {
6060 "protocol": "fuchsia.echo.Echo",
6061 "delivery": "on_readable",
6062 }
6063 ],
6064 });
6065 assert_matches!(
6066 compile(&input, CompileOptions::new()),
6067 Err(Error::RestrictedFeature(feature))
6068 if feature == "delivery_type"
6069 );
6070 }
6071}