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