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