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