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