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, 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, AsClause, AsClauseContext, Availability, Capability, CapabilityClause, ConfigKey,
27 ConfigNestedValueType, ConfigRuntimeSource, ConfigType, ConfigValueType, ContextCapability,
28 ContextPathClause, ContextSpanned, DictionaryRef, EventScope, FromClause, FromClauseContext,
29 OneOrMany, Path, PathClause, Program, ResolverRegistration, RootDictionaryRef,
30 SourceAvailability, validate,
31};
32use cm_rust::NativeIntoFidl;
33use cm_types::{self as cm, BorrowedName, Name, StartupMode};
34use directed_graph::DirectedGraph;
35use indexmap::IndexMap;
36use itertools::Itertools;
37use serde_json::{Map, Value};
38use sha2::{Digest, Sha256};
39use std::collections::{BTreeMap, BTreeSet};
40use std::convert::{Into, TryInto};
41use std::path::PathBuf;
42use std::sync::Arc;
43use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio};
44
45#[derive(Default, Clone)]
47pub struct CompileOptions<'a> {
48 file: Option<PathBuf>,
49 config_package_path: Option<String>,
50 features: Option<&'a FeatureSet>,
51 capability_requirements: CapabilityRequirements<'a>,
52}
53
54impl<'a> CompileOptions<'a> {
55 pub fn new() -> Self {
56 Default::default()
57 }
58
59 pub fn file(mut self, file: &std::path::Path) -> CompileOptions<'a> {
61 self.file = Some(file.to_path_buf());
62 self
63 }
64
65 pub fn config_package_path(mut self, config_package_path: &str) -> CompileOptions<'a> {
67 self.config_package_path = Some(config_package_path.to_string());
68 self
69 }
70
71 pub fn features(mut self, features: &'a FeatureSet) -> CompileOptions<'a> {
73 self.features = Some(features);
74 self
75 }
76
77 pub fn protocol_requirements(
80 mut self,
81 protocol_requirements: CapabilityRequirements<'a>,
82 ) -> CompileOptions<'a> {
83 self.capability_requirements = protocol_requirements;
84 self
85 }
86}
87
88pub fn compile(
95 document: &Document,
96 options: CompileOptions<'_>,
97) -> Result<fdecl::Component, Error> {
98 validate::validate_cml(
99 &document,
100 options.file.as_ref().map(PathBuf::as_path),
101 options.features.unwrap_or(&FeatureSet::empty()),
102 &options.capability_requirements,
103 )?;
104
105 let all_capability_names: BTreeSet<&BorrowedName> =
106 document.all_capability_names().into_iter().collect();
107 let all_children = document.all_children_names().into_iter().collect();
108 let all_collections = document.all_collection_names().into_iter().collect();
109 let component = fdecl::Component {
110 program: document.program.as_ref().map(translate_program_deprecated).transpose()?,
111 uses: document
112 .r#use
113 .as_ref()
114 .map(|u| {
115 translate_use_deprecated(
116 &options,
117 u,
118 &all_capability_names,
119 &all_children,
120 &all_collections,
121 )
122 })
123 .transpose()?,
124 exposes: document
125 .expose
126 .as_ref()
127 .map(|e| {
128 translate_expose_deprecated(
129 &options,
130 e,
131 &all_capability_names,
132 &all_collections,
133 &all_children,
134 )
135 })
136 .transpose()?,
137 offers: document
138 .offer
139 .as_ref()
140 .map(|offer| {
141 translate_offer_deprecated(
142 &options,
143 offer,
144 &all_capability_names,
145 &all_children,
146 &all_collections,
147 )
148 })
149 .transpose()?,
150 capabilities: document
151 .capabilities
152 .as_ref()
153 .map(|c| translate_capabilities_deprecated(&options, c, false))
154 .transpose()?,
155 children: document.children.as_ref().map(translate_children_deprecated),
156 collections: document.collections.as_ref().map(translate_collections_deprecated),
157 environments: document
158 .environments
159 .as_ref()
160 .map(|env| translate_environments_deprecated(&options, env, &all_capability_names))
161 .transpose()?,
162 facets: document.facets.clone().map(dictionary_from_nested_map).transpose()?,
163 config: translate_config_deprecated(
164 &document.config,
165 &document.r#use,
166 &options.config_package_path,
167 )?,
168 ..Default::default()
169 };
170
171 let mut deps = DirectedGraph::new();
172 cm_fidl_validator::validate(&component, &mut deps).map_err(Error::fidl_validator)?;
173
174 Ok(component)
175}
176
177pub fn compile_context(
179 document: &DocumentContext,
180 options: CompileOptions<'_>,
181) -> Result<fdecl::Component, Error> {
182 validate::validate_cml_context(
183 &document,
184 options.features.unwrap_or(&FeatureSet::empty()),
185 &options.capability_requirements,
186 )?;
187
188 let all_capability_names: BTreeSet<Name> =
189 document.all_capability_names().iter().cloned().collect();
190 let all_children: BTreeSet<Name> = document.all_children_names().iter().cloned().collect();
191 let all_collections: BTreeSet<Name> = document.all_collection_names().iter().cloned().collect();
192
193 let component = fdecl::Component {
194 program: document.program.as_ref().map(|p| translate_program(&p.value)).transpose()?,
195
196 uses: document
197 .r#use
198 .as_ref()
199 .map(|u| {
200 translate_use(&options, u, &all_capability_names, &all_children, &all_collections)
201 })
202 .transpose()?,
203
204 exposes: document
205 .expose
206 .as_ref()
207 .map(|e| {
208 translate_expose(
209 &options,
210 e,
211 &all_capability_names,
212 &all_collections,
213 &all_children,
214 )
215 })
216 .transpose()?,
217
218 offers: document
219 .offer
220 .as_ref()
221 .map(|o| {
222 translate_offer(&options, o, &all_capability_names, &all_children, &all_collections)
223 })
224 .transpose()?,
225
226 capabilities: document
227 .capabilities
228 .as_ref()
229 .map(|c| translate_capabilities(&options, c, false))
230 .transpose()?,
231
232 children: document.children.as_ref().map(|c| translate_children(c)),
233 collections: document.collections.as_ref().map(|c| translate_collections(c)),
234
235 environments: document
236 .environments
237 .as_ref()
238 .map(|env| translate_environments(&options, env, &all_capability_names))
239 .transpose()?,
240 facets: document.facets.clone().map(dictionary_from_nested_spanned_map).transpose()?,
241
242 config: translate_config(&document.config, &document.r#use, &options.config_package_path)?,
243 ..Default::default()
244 };
245
246 let mut deps = DirectedGraph::new();
247 cm_fidl_validator::validate(&component, &mut deps).map_err(Error::fidl_validator)?;
248
249 Ok(component)
250}
251
252pub fn dictionary_from_context_map(
254 map: IndexMap<String, ContextSpanned<Value>>,
255) -> Result<fdata::Dictionary, Error> {
256 let mut entries = Vec::new();
257
258 for (key, spanned_value) in map {
259 let dictionary_value =
261 value_to_dictionary_value(spanned_value.value, &spanned_value.origin)?;
262
263 entries.push(fdata::DictionaryEntry { key, value: dictionary_value });
264 }
265
266 entries.sort_by(|a, b| a.key.cmp(&b.key));
269
270 Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
271}
272
273fn dictionary_from_map(in_obj: Map<String, Value>) -> Result<fdata::Dictionary, Error> {
275 let mut entries = vec![];
276 for (key, v) in in_obj {
277 let value = value_to_dictionary_value_deprecated(v)?;
278 entries.push(fdata::DictionaryEntry { key, value });
279 }
280 Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
281}
282
283fn value_to_dictionary_value_deprecated(
285 value: Value,
286) -> Result<Option<Box<fdata::DictionaryValue>>, Error> {
287 match value {
288 Value::Null => Ok(None),
289 Value::String(s) => Ok(Some(Box::new(fdata::DictionaryValue::Str(s)))),
290 Value::Array(arr) => {
291 if arr.iter().all(Value::is_string) {
292 let strs =
293 arr.into_iter().map(|v| v.as_str().unwrap().to_owned()).collect::<Vec<_>>();
294 Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs))))
295 } else if arr.iter().all(Value::is_object) {
296 let objs = arr
297 .into_iter()
298 .map(|v| v.as_object().unwrap().clone())
299 .map(|v| dictionary_from_nested_map(v.into_iter().collect()))
300 .collect::<Result<Vec<_>, _>>()?;
301 Ok(Some(Box::new(fdata::DictionaryValue::ObjVec(objs))))
302 } else {
303 Err(Error::validate(
304 "Values of an array must either exclusively strings or exclusively objects",
305 ))
306 }
307 }
308 other => Err(Error::validate(format!(
309 "Value must be string, list of strings, or list of objects: {:?}",
310 other
311 ))),
312 }
313}
314
315fn value_to_dictionary_value(
316 value: Value,
317 origin: &Arc<PathBuf>,
318) -> Result<Option<Box<fdata::DictionaryValue>>, Error> {
319 match value {
320 Value::Null => Ok(None),
321 Value::String(s) => Ok(Some(Box::new(fdata::DictionaryValue::Str(s)))),
322 Value::Array(arr) => {
323 if arr.is_empty() {
324 return Ok(Some(Box::new(fdata::DictionaryValue::StrVec(vec![]))));
325 }
326
327 if arr.iter().all(Value::is_string) {
328 let strs =
329 arr.into_iter().map(|v| v.as_str().unwrap().to_owned()).collect::<Vec<_>>();
330 Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs))))
331 } else if arr.iter().all(Value::is_object) {
332 let objs = arr
333 .into_iter()
334 .map(|v| {
335 let obj_map = v.as_object().unwrap().clone().into_iter().collect();
336 dictionary_from_nested_map(obj_map)
337 })
338 .collect::<Result<Vec<_>, _>>()?;
339 Ok(Some(Box::new(fdata::DictionaryValue::ObjVec(objs))))
340 } else {
341 Err(Error::validate_context(
342 "Values of an array must be exclusively strings or exclusively objects",
343 Some(origin.clone()),
344 ))
345 }
346 }
347 other => Err(Error::validate_context(
348 format!("Value must be string, list of strings, or list of objects: {:?}", other),
349 Some(origin.clone()),
350 )),
351 }
352}
353
354fn dictionary_from_nested_map(map: IndexMap<String, Value>) -> Result<fdata::Dictionary, Error> {
389 fn key_value_to_entries(
390 key: String,
391 value: Value,
392 ) -> Result<Vec<fdata::DictionaryEntry>, Error> {
393 if let Value::Object(map) = value {
394 let entries = map
395 .into_iter()
396 .map(|(k, v)| key_value_to_entries([key.clone(), ".".to_string(), k].concat(), v))
397 .collect::<Result<Vec<_>, _>>()?
398 .into_iter()
399 .flatten()
400 .collect();
401 return Ok(entries);
402 }
403
404 let entry_value = value_to_dictionary_value_deprecated(value)?;
405 Ok(vec![fdata::DictionaryEntry { key, value: entry_value }])
406 }
407
408 let entries = map
409 .into_iter()
410 .map(|(k, v)| key_value_to_entries(k, v))
411 .collect::<Result<Vec<_>, _>>()?
412 .into_iter()
413 .flatten()
414 .collect();
415 Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
416}
417
418fn dictionary_from_nested_spanned_map(
419 map: IndexMap<String, ContextSpanned<Value>>,
420) -> Result<fdata::Dictionary, Error> {
421 fn key_value_to_entries(
422 key: String,
423 value: Value,
424 ) -> Result<Vec<fdata::DictionaryEntry>, Error> {
425 if let Value::Object(map) = value {
426 let entries = map
427 .into_iter()
428 .map(|(k, v)| key_value_to_entries([key.clone(), ".".to_string(), k].concat(), v))
429 .collect::<Result<Vec<_>, _>>()?
430 .into_iter()
431 .flatten()
432 .collect();
433 return Ok(entries);
434 }
435
436 let entry_value = value_to_dictionary_value_deprecated(value)?;
437 Ok(vec![fdata::DictionaryEntry { key, value: entry_value }])
438 }
439
440 let entries = map
441 .into_iter()
442 .map(|(k, v)| key_value_to_entries(k, v.value))
443 .collect::<Result<Vec<_>, _>>()?
444 .into_iter()
445 .flatten()
446 .collect();
447 Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
448}
449
450fn translate_program_deprecated(program: &Program) -> Result<fdecl::Program, Error> {
452 Ok(fdecl::Program {
453 runner: program.runner.as_ref().map(|r| r.to_string()),
454 info: Some(dictionary_from_nested_map(program.info.clone())?),
455 ..Default::default()
456 })
457}
458
459fn translate_program(program: &ContextProgram) -> Result<fdecl::Program, Error> {
461 Ok(fdecl::Program {
462 runner: program.runner.as_ref().map(|r| r.value.clone().into()),
463 info: Some(dictionary_from_nested_map(program.info.clone())?),
464 ..Default::default()
465 })
466}
467
468fn translate_use_deprecated(
470 options: &CompileOptions<'_>,
471 use_in: &Vec<Use>,
472 all_capability_names: &BTreeSet<&BorrowedName>,
473 all_children: &BTreeSet<&BorrowedName>,
474 all_collections: &BTreeSet<&BorrowedName>,
475) -> Result<Vec<fdecl::Use>, Error> {
476 let mut out_uses = vec![];
477 for use_ in use_in {
478 if let Some(n) = use_.service() {
479 let (source, source_dictionary) = extract_use_source_deprecated(
480 options,
481 use_,
482 all_capability_names,
483 all_children,
484 Some(all_collections),
485 )?;
486 let target_paths = all_target_use_paths_deprecated(use_, use_)
487 .ok_or_else(|| Error::internal("no capability"))?;
488 let source_names = n.into_iter();
489 let availability = extract_use_availability_deprecated(use_)?;
490 for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
491 {
492 out_uses.push(fdecl::Use::Service(fdecl::UseService {
493 source: Some(source.clone()),
494 source_name: Some(source_name.to_string()),
495 source_dictionary: source_dictionary.clone(),
496 target_path: Some(target_path.to_string()),
497 dependency_type: Some(
498 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
499 ),
500 availability: Some(availability),
501 ..Default::default()
502 }));
503 }
504 } else if let Some(n) = use_.protocol() {
505 let (source, source_dictionary) = extract_use_source_deprecated(
506 options,
507 use_,
508 all_capability_names,
509 all_children,
510 None,
511 )?;
512 let availability = extract_use_availability_deprecated(use_)?;
513 if use_.numbered_handle.is_some() {
514 let OneOrMany::One(source_name) = n else {
515 panic!("numbered_handle: multiple source_name");
516 };
517 out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
518 source: Some(source.clone()),
519 source_name: Some(source_name.to_string()),
520 source_dictionary: source_dictionary.clone(),
521 target_path: None,
522 numbered_handle: use_.numbered_handle.map(Into::into),
523 dependency_type: Some(
524 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
525 ),
526 availability: Some(availability),
527 ..Default::default()
528 }));
529 continue;
530 }
531 let source_names = n.into_iter();
532 let target_paths = all_target_use_paths_deprecated(use_, use_)
533 .ok_or_else(|| Error::internal("no capability"))?;
534 for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
535 {
536 out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
537 source: Some(source.clone()),
538 source_name: Some(source_name.to_string()),
539 source_dictionary: source_dictionary.clone(),
540 target_path: Some(target_path.into()),
541 numbered_handle: use_.numbered_handle.map(Into::into),
542 dependency_type: Some(
543 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
544 ),
545 availability: Some(availability),
546 ..Default::default()
547 }));
548 }
549 } else if let Some(n) = &use_.directory {
550 let (source, source_dictionary) = extract_use_source_deprecated(
551 options,
552 use_,
553 all_capability_names,
554 all_children,
555 None,
556 )?;
557 let target_path = one_target_use_path_deprecated(use_, use_)?;
558 let rights = extract_required_rights(use_, "use")?;
559 let subdir = extract_use_subdir_deprecated(use_);
560 let availability = extract_use_availability_deprecated(use_)?;
561 out_uses.push(fdecl::Use::Directory(fdecl::UseDirectory {
562 source: Some(source),
563 source_name: Some(n.clone().into()),
564 source_dictionary,
565 target_path: Some(target_path.into()),
566 rights: Some(rights),
567 subdir: subdir.map(|s| s.into()),
568 dependency_type: Some(
569 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
570 ),
571 availability: Some(availability),
572 ..Default::default()
573 }));
574 } else if let Some(n) = &use_.storage {
575 let target_path = one_target_use_path_deprecated(use_, use_)?;
576 let availability = extract_use_availability_deprecated(use_)?;
577 out_uses.push(fdecl::Use::Storage(fdecl::UseStorage {
578 source_name: Some(n.clone().into()),
579 target_path: Some(target_path.into()),
580 availability: Some(availability),
581 ..Default::default()
582 }));
583 } else if let Some(names) = &use_.event_stream {
584 let source_names: Vec<String> =
585 annotate_type::<Vec<cm_types::Name>>(names.clone().into())
586 .iter()
587 .map(|name| name.to_string())
588 .collect();
589 let availability = extract_use_availability_deprecated(use_)?;
590 for name in source_names {
591 let scopes = match use_.scope.clone() {
592 Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
593 None => None,
594 };
595 let internal_error = format!(
596 "Internal error in all_target_use_paths_deprecated when translating an EventStream. \
597 Please file a bug."
598 );
599 let (source, _source_dictionary) = extract_use_source_deprecated(
600 options,
601 use_,
602 all_capability_names,
603 all_children,
604 None,
605 )?;
606 out_uses.push(fdecl::Use::EventStream(fdecl::UseEventStream {
607 source_name: Some(name),
608 scope: match scopes {
609 Some(values) => {
610 let mut output = vec![];
611 for value in &values {
612 static EMPTY_SET: BTreeSet<&BorrowedName> = BTreeSet::new();
613 if let Some(target) = translate_target_ref_deprecated(
614 options,
615 value.into(),
616 &all_children,
617 &all_collections,
618 &EMPTY_SET,
619 Some(&TargetAvailability::Required),
620 )? {
621 output.push(target);
622 }
623 }
624 Some(output)
625 }
626 None => None,
627 },
628 source: Some(source),
629 target_path: Some(
630 annotate_type::<Vec<cm_types::Path>>(
631 all_target_use_paths_deprecated(use_, use_)
632 .ok_or_else(|| Error::internal(internal_error.clone()))?
633 .into(),
634 )
635 .iter()
636 .next()
637 .ok_or_else(|| Error::internal(internal_error.clone()))?
638 .to_string(),
639 ),
640 filter: match use_.filter.clone() {
641 Some(dict) => Some(dictionary_from_map(dict)?),
642 None => None,
643 },
644 availability: Some(availability),
645 ..Default::default()
646 }));
647 }
648 } else if let Some(n) = &use_.runner {
649 let (source, source_dictionary) = extract_use_source_deprecated(
650 &options,
651 use_,
652 all_capability_names,
653 all_children,
654 None,
655 )?;
656 #[cfg(fuchsia_api_level_at_least = "HEAD")]
657 out_uses.push(fdecl::Use::Runner(fdecl::UseRunner {
658 source: Some(source),
659 source_name: Some(n.clone().into()),
660 source_dictionary,
661 ..Default::default()
662 }));
663 } else if let Some(n) = &use_.config {
664 let (source, source_dictionary) = extract_use_source_deprecated(
665 &options,
666 use_,
667 all_capability_names,
668 all_children,
669 None,
670 )?;
671 let target = match &use_.key {
672 None => return Err(Error::validate("\"use config\" must have \"key\" field set.")),
673 Some(t) => t.clone(),
674 };
675 let availability = extract_use_availability_deprecated(use_)?;
676 let type_ = validate::use_config_to_value_type(use_)?;
677
678 let default = if let Some(default) = &use_.config_default {
679 let value = config_value_file::field::config_value_from_json_value(
680 default,
681 &type_.clone().into(),
682 )
683 .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", n, e)))?;
684 Some(value.native_into_fidl())
685 } else {
686 None
687 };
688
689 out_uses.push(fdecl::Use::Config(fdecl::UseConfiguration {
690 source: Some(source),
691 source_name: Some(n.clone().into()),
692 target_name: Some(target.into()),
693 availability: Some(availability),
694 type_: Some(translate_value_type(&type_).0),
695 default,
696 source_dictionary,
697 ..Default::default()
698 }));
699 } else if let Some(n) = &use_.dictionary {
700 let (source, source_dictionary) = extract_use_source_deprecated(
701 options,
702 use_,
703 all_capability_names,
704 all_children,
705 None,
706 )?;
707 let availability = extract_use_availability_deprecated(use_)?;
708 for source_name in n.into_iter() {
709 out_uses.push(fdecl::Use::Dictionary(fdecl::UseDictionary {
710 source: Some(source.clone()),
711 source_name: Some(source_name.to_string()),
712 source_dictionary: source_dictionary.clone(),
713 target_path: Some(
714 use_.path().as_ref().expect("no path on use dictionary").to_string(),
715 ),
716 dependency_type: Some(
717 use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
718 ),
719 availability: Some(availability),
720 ..Default::default()
721 }));
722 }
723 } else {
724 return Err(Error::internal(format!("no capability in use declaration")));
725 };
726 }
727 Ok(out_uses)
728}
729
730fn translate_use(
732 options: &CompileOptions<'_>,
733 use_in: &Vec<ContextSpanned<ContextUse>>,
734 all_capability_names: &BTreeSet<Name>,
735 all_children: &BTreeSet<Name>,
736 all_collections: &BTreeSet<Name>,
737) -> Result<Vec<fdecl::Use>, Error> {
738 let mut out_uses = vec![];
739 for spanned_use in use_in {
740 let use_ = &spanned_use.value;
741 if let Some(spanned) = use_.service() {
742 let n = spanned.value;
743 let (source, source_dictionary) = extract_use_source(
744 options,
745 use_,
746 all_capability_names,
747 all_children,
748 Some(all_collections),
749 )?;
750 let target_paths =
751 all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
752 let source_names = n.into_iter();
753 let availability = extract_use_availability(use_)?;
754 for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
755 {
756 out_uses.push(fdecl::Use::Service(fdecl::UseService {
757 source: Some(source.clone()),
758 source_name: Some(source_name.to_string()),
759 source_dictionary: source_dictionary.clone(),
760 target_path: Some(target_path.to_string()),
761 dependency_type: Some(
762 use_.dependency
763 .clone()
764 .map(|s| s.value)
765 .unwrap_or(cm::DependencyType::Strong)
766 .into(),
767 ),
768 availability: Some(availability),
769 ..Default::default()
770 }));
771 }
772 } else if let Some(spanned) = use_.protocol() {
773 let n = spanned.value;
774 let (source, source_dictionary) =
775 extract_use_source(options, use_, all_capability_names, all_children, None)?;
776 let availability = extract_use_availability(use_)?;
777 if use_.numbered_handle.is_some() {
778 let OneOrMany::One(source_name) = n else {
779 panic!("numbered_handle: multiple source_name");
780 };
781 out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
782 source: Some(source.clone()),
783 source_name: Some(source_name.to_string()),
784 source_dictionary: source_dictionary.clone(),
785 target_path: None,
786 numbered_handle: use_.numbered_handle.as_ref().map(|s| s.value.into()),
787 dependency_type: Some(
788 use_.dependency
789 .clone()
790 .map(|s| s.value)
791 .unwrap_or(cm::DependencyType::Strong)
792 .into(),
793 ),
794 availability: Some(availability),
795 ..Default::default()
796 }));
797 continue;
798 }
799 let source_names = n.into_iter();
800 let target_paths =
801 all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
802 for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
803 {
804 out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
805 source: Some(source.clone()),
806 source_name: Some(source_name.to_string()),
807 source_dictionary: source_dictionary.clone(),
808 target_path: Some(target_path.into()),
809 numbered_handle: use_.numbered_handle.as_ref().map(|s| s.value.into()),
810 dependency_type: Some(
811 use_.dependency
812 .clone()
813 .map(|s| s.value)
814 .unwrap_or(cm::DependencyType::Strong)
815 .into(),
816 ),
817 availability: Some(availability),
818 ..Default::default()
819 }));
820 }
821 } else if let Some(spanned) = &use_.directory {
822 let n = &spanned.value;
823 let (source, source_dictionary) =
824 extract_use_source(options, use_, all_capability_names, all_children, None)?;
825 let target_path = one_target_use_path(use_, use_)?;
826 let rights = extract_required_rights(use_, "use")?;
827 let subdir = extract_use_subdir(use_);
828 let availability = extract_use_availability(use_)?;
829 out_uses.push(fdecl::Use::Directory(fdecl::UseDirectory {
830 source: Some(source),
831 source_name: Some(n.clone().into()),
832 source_dictionary,
833 target_path: Some(target_path.into()),
834 rights: Some(rights),
835 subdir: subdir.map(|s| s.into()),
836 dependency_type: Some(
837 use_.dependency
838 .clone()
839 .map(|s| s.value)
840 .unwrap_or(cm::DependencyType::Strong)
841 .into(),
842 ),
843 availability: Some(availability),
844 ..Default::default()
845 }));
846 } else if let Some(spanned) = &use_.storage {
847 let n = &spanned.value;
848 let target_path = one_target_use_path(use_, use_)?;
849 let availability = extract_use_availability(use_)?;
850 out_uses.push(fdecl::Use::Storage(fdecl::UseStorage {
851 source_name: Some(n.clone().into()),
852 target_path: Some(target_path.into()),
853 availability: Some(availability),
854 ..Default::default()
855 }));
856 } else if let Some(spanned_names) = &use_.event_stream {
857 let names = &spanned_names.value;
858 let source_names: Vec<String> =
859 annotate_type::<Vec<cm_types::Name>>(names.clone().into())
860 .iter()
861 .map(|name| name.to_string())
862 .collect();
863 let availability = extract_use_availability(use_)?;
864 for name in source_names {
865 let scopes = match use_.scope.clone() {
866 Some(v) => Some(annotate_type::<Vec<EventScope>>(v.value.into())),
867 None => None,
868 };
869 let internal_error = format!(
870 "Internal error in all_target_use_paths when translating an EventStream. \
871 Please file a bug."
872 );
873 let (source, _source_dictionary) =
874 extract_use_source(options, use_, all_capability_names, all_children, None)?;
875 out_uses.push(fdecl::Use::EventStream(fdecl::UseEventStream {
876 source_name: Some(name),
877 scope: match scopes {
878 Some(values) => {
879 let mut output = vec![];
880 for value in &values {
881 if let Some(target) = translate_target_ref(
882 options,
883 value.into(),
884 &all_children,
885 &all_collections,
886 &BTreeSet::new(),
887 Some(&TargetAvailability::Required),
888 )? {
889 output.push(target);
890 }
891 }
892 Some(output)
893 }
894 None => None,
895 },
896 source: Some(source),
897 target_path: Some(
898 annotate_type::<Vec<cm_types::Path>>(
899 all_target_use_paths(use_, use_)
900 .ok_or_else(|| Error::internal(internal_error.clone()))?
901 .into(),
902 )
903 .iter()
904 .next()
905 .ok_or_else(|| Error::internal(internal_error.clone()))?
906 .to_string(),
907 ),
908 filter: match use_.filter.clone() {
909 Some(dict) => Some(dictionary_from_map(dict.value)?),
910 None => None,
911 },
912 availability: Some(availability),
913 ..Default::default()
914 }));
915 }
916 } else if let Some(spanned) = &use_.runner {
917 let n = &spanned.value;
918 let (source, source_dictionary) =
919 extract_use_source(&options, use_, all_capability_names, all_children, None)?;
920 #[cfg(fuchsia_api_level_at_least = "HEAD")]
921 out_uses.push(fdecl::Use::Runner(fdecl::UseRunner {
922 source: Some(source),
923 source_name: Some(n.clone().into()),
924 source_dictionary,
925 ..Default::default()
926 }));
927 } else if let Some(spanned) = &use_.config {
928 let n = &spanned.value;
929 let (source, source_dictionary) =
930 extract_use_source(&options, use_, all_capability_names, all_children, None)?;
931 let target = match &use_.key {
932 None => {
933 return Err(Error::validate_context(
934 "\"use config\" must have \"key\" field set.",
935 Some(spanned.origin.clone()),
936 ));
937 }
938 Some(t) => t.clone(),
939 };
940 let availability = extract_use_availability(use_)?;
941 let type_ = validate::use_config_to_value_type_context(use_)?;
942
943 let default = if let Some(default) = &use_.config_default {
944 let value = config_value_file::field::config_value_from_json_value(
945 &default.value,
946 &type_.clone().into(),
947 )
948 .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", n, e)))?;
949 Some(value.native_into_fidl())
950 } else {
951 None
952 };
953
954 out_uses.push(fdecl::Use::Config(fdecl::UseConfiguration {
955 source: Some(source),
956 source_name: Some(n.clone().into()),
957 target_name: Some(target.value.into()),
958 availability: Some(availability),
959 type_: Some(translate_value_type(&type_).0),
960 default,
961 source_dictionary,
962 ..Default::default()
963 }));
964 } else if let Some(n) = &use_.dictionary {
965 let (source, source_dictionary) =
966 extract_use_source(options, use_, all_capability_names, all_children, None)?;
967 let availability = extract_use_availability(use_)?;
968 for source_name in n.value.clone().into_iter() {
969 out_uses.push(fdecl::Use::Dictionary(fdecl::UseDictionary {
970 source: Some(source.clone()),
971 source_name: Some(source_name.to_string()),
972 source_dictionary: source_dictionary.clone(),
973 target_path: Some(
974 use_.path().as_ref().expect("no path on use dictionary").value.to_string(),
975 ),
976 dependency_type: Some(
977 use_.dependency
978 .clone()
979 .map(|s| s.value)
980 .unwrap_or(cm::DependencyType::Strong)
981 .into(),
982 ),
983 availability: Some(availability),
984 ..Default::default()
985 }));
986 }
987 } else {
988 return Err(Error::internal(format!("no capability in use declaration")));
989 };
990 }
991 Ok(out_uses)
992}
993
994fn translate_expose_deprecated(
997 options: &CompileOptions<'_>,
998 expose_in: &Vec<Expose>,
999 all_capability_names: &BTreeSet<&BorrowedName>,
1000 all_collections: &BTreeSet<&BorrowedName>,
1001 all_children: &BTreeSet<&BorrowedName>,
1002) -> Result<Vec<fdecl::Expose>, Error> {
1003 let mut out_exposes = vec![];
1004 for expose in expose_in.iter() {
1005 let target = extract_expose_target_deprecated(expose);
1006 if let Some(source_names) = expose.service() {
1007 let sources =
1010 extract_all_expose_sources_deprecated(options, expose, Some(all_collections));
1011 let target_names = all_target_capability_names_deprecated(expose, expose)
1012 .ok_or_else(|| Error::internal("no capability"))?;
1013 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1014 {
1015 for (source, source_dictionary) in &sources {
1016 let DerivedSourceInfo { source, source_dictionary, availability } =
1017 derive_source_and_availability_deprecated(
1018 expose.availability.as_ref(),
1019 source.clone(),
1020 source_dictionary.clone(),
1021 expose.source_availability.as_ref(),
1022 all_capability_names,
1023 all_children,
1024 all_collections,
1025 );
1026 out_exposes.push(fdecl::Expose::Service(fdecl::ExposeService {
1027 source: Some(source),
1028 source_name: Some(source_name.to_string()),
1029 source_dictionary,
1030 target_name: Some(target_name.to_string()),
1031 target: Some(target.clone()),
1032 availability: Some(availability),
1033 ..Default::default()
1034 }))
1035 }
1036 }
1037 } else if let Some(n) = expose.protocol() {
1038 let (source, source_dictionary) = extract_single_expose_source_deprecated(
1039 options,
1040 expose,
1041 Some(all_capability_names),
1042 )?;
1043 let source_names = n.into_iter();
1044 let target_names = all_target_capability_names_deprecated(expose, expose)
1045 .ok_or_else(|| Error::internal("no capability"))?;
1046 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1047 {
1048 let DerivedSourceInfo { source, source_dictionary, availability } =
1049 derive_source_and_availability_deprecated(
1050 expose.availability.as_ref(),
1051 source.clone(),
1052 source_dictionary.clone(),
1053 expose.source_availability.as_ref(),
1054 all_capability_names,
1055 all_children,
1056 all_collections,
1057 );
1058 out_exposes.push(fdecl::Expose::Protocol(fdecl::ExposeProtocol {
1059 source: Some(source),
1060 source_name: Some(source_name.to_string()),
1061 source_dictionary,
1062 target_name: Some(target_name.to_string()),
1063 target: Some(target.clone()),
1064 availability: Some(availability),
1065 ..Default::default()
1066 }))
1067 }
1068 } else if let Some(n) = expose.directory() {
1069 let (source, source_dictionary) =
1070 extract_single_expose_source_deprecated(options, expose, None)?;
1071 let source_names = n.into_iter();
1072 let target_names = all_target_capability_names_deprecated(expose, expose)
1073 .ok_or_else(|| Error::internal("no capability"))?;
1074 let rights = extract_expose_rights_deprecated(expose)?;
1075 let subdir = extract_expose_subdir_deprecated(expose);
1076 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1077 {
1078 let DerivedSourceInfo { source, source_dictionary, availability } =
1079 derive_source_and_availability_deprecated(
1080 expose.availability.as_ref(),
1081 source.clone(),
1082 source_dictionary.clone(),
1083 expose.source_availability.as_ref(),
1084 all_capability_names,
1085 all_children,
1086 all_collections,
1087 );
1088 out_exposes.push(fdecl::Expose::Directory(fdecl::ExposeDirectory {
1089 source: Some(source),
1090 source_name: Some(source_name.to_string()),
1091 source_dictionary,
1092 target_name: Some(target_name.to_string()),
1093 target: Some(target.clone()),
1094 rights,
1095 subdir: subdir.as_ref().map(|s| s.clone().into()),
1096 availability: Some(availability),
1097 ..Default::default()
1098 }))
1099 }
1100 } else if let Some(n) = expose.runner() {
1101 let (source, source_dictionary) =
1102 extract_single_expose_source_deprecated(options, expose, None)?;
1103 let source_names = n.into_iter();
1104 let target_names = all_target_capability_names_deprecated(expose, expose)
1105 .ok_or_else(|| Error::internal("no capability"))?;
1106 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1107 {
1108 out_exposes.push(fdecl::Expose::Runner(fdecl::ExposeRunner {
1109 source: Some(source.clone()),
1110 source_name: Some(source_name.to_string()),
1111 source_dictionary: source_dictionary.clone(),
1112 target: Some(target.clone()),
1113 target_name: Some(target_name.to_string()),
1114 ..Default::default()
1115 }))
1116 }
1117 } else if let Some(n) = expose.resolver() {
1118 let (source, source_dictionary) =
1119 extract_single_expose_source_deprecated(options, expose, None)?;
1120 let source_names = n.into_iter();
1121 let target_names = all_target_capability_names_deprecated(expose, expose)
1122 .ok_or_else(|| Error::internal("no capability"))?;
1123 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1124 {
1125 out_exposes.push(fdecl::Expose::Resolver(fdecl::ExposeResolver {
1126 source: Some(source.clone()),
1127 source_name: Some(source_name.to_string()),
1128 source_dictionary: source_dictionary.clone(),
1129 target: Some(target.clone()),
1130 target_name: Some(target_name.to_string()),
1131 ..Default::default()
1132 }))
1133 }
1134 } else if let Some(n) = expose.dictionary() {
1135 let (source, source_dictionary) =
1136 extract_single_expose_source_deprecated(options, expose, None)?;
1137 let source_names = n.into_iter();
1138 let target_names = all_target_capability_names_deprecated(expose, expose)
1139 .ok_or_else(|| Error::internal("no capability"))?;
1140 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1141 {
1142 let DerivedSourceInfo { source, source_dictionary, availability } =
1143 derive_source_and_availability_deprecated(
1144 expose.availability.as_ref(),
1145 source.clone(),
1146 source_dictionary.clone(),
1147 expose.source_availability.as_ref(),
1148 all_capability_names,
1149 all_children,
1150 all_collections,
1151 );
1152 out_exposes.push(fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
1153 source: Some(source),
1154 source_name: Some(source_name.to_string()),
1155 source_dictionary,
1156 target_name: Some(target_name.to_string()),
1157 target: Some(target.clone()),
1158 availability: Some(availability),
1159 ..Default::default()
1160 }))
1161 }
1162 } else if let Some(n) = expose.config() {
1163 let (source, source_dictionary) =
1164 extract_single_expose_source_deprecated(options, expose, None)?;
1165 let source_names = n.into_iter();
1166 let target_names = all_target_capability_names_deprecated(expose, expose)
1167 .ok_or_else(|| Error::internal("no capability"))?;
1168 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1169 {
1170 let DerivedSourceInfo { source, source_dictionary, availability } =
1171 derive_source_and_availability_deprecated(
1172 expose.availability.as_ref(),
1173 source.clone(),
1174 source_dictionary.clone(),
1175 expose.source_availability.as_ref(),
1176 all_capability_names,
1177 all_children,
1178 all_collections,
1179 );
1180 out_exposes.push(fdecl::Expose::Config(fdecl::ExposeConfiguration {
1181 source: Some(source.clone()),
1182 source_name: Some(source_name.to_string()),
1183 source_dictionary,
1184 target: Some(target.clone()),
1185 target_name: Some(target_name.to_string()),
1186 availability: Some(availability),
1187 ..Default::default()
1188 }))
1189 }
1190 } else {
1191 return Err(Error::internal(format!("expose: must specify a known capability")));
1192 }
1193 }
1194 Ok(out_exposes)
1195}
1196
1197fn translate_expose(
1200 options: &CompileOptions<'_>,
1201 expose_in: &Vec<ContextSpanned<ContextExpose>>,
1202 all_capability_names: &BTreeSet<Name>,
1203 all_collections: &BTreeSet<Name>,
1204 all_children: &BTreeSet<Name>,
1205) -> Result<Vec<fdecl::Expose>, Error> {
1206 let mut out_exposes = vec![];
1207 for spanned_expose in expose_in.iter() {
1208 let expose = &spanned_expose.value;
1209 let target = extract_expose_target(expose);
1210 if let Some(source_names) = expose.service() {
1211 let sources = extract_all_expose_sources(options, expose, Some(all_collections));
1214 let target_names = all_target_capability_names(expose, expose)
1215 .ok_or_else(|| Error::internal("no capability"))?;
1216 for (source_name, target_name) in
1217 source_names.value.into_iter().zip(target_names.into_iter())
1218 {
1219 for (source, source_dictionary) in &sources {
1220 let DerivedSourceInfo { source, source_dictionary, availability } =
1221 derive_source_and_availability(
1222 expose.availability.as_ref(),
1223 source.clone(),
1224 source_dictionary.clone(),
1225 expose.source_availability.as_ref(),
1226 all_capability_names,
1227 all_children,
1228 all_collections,
1229 );
1230 out_exposes.push(fdecl::Expose::Service(fdecl::ExposeService {
1231 source: Some(source),
1232 source_name: Some(source_name.to_string()),
1233 source_dictionary,
1234 target_name: Some(target_name.to_string()),
1235 target: Some(target.clone()),
1236 availability: Some(availability),
1237 ..Default::default()
1238 }))
1239 }
1240 }
1241 } else if let Some(n) = expose.protocol() {
1242 let (source, source_dictionary) =
1243 extract_single_expose_source(options, expose, Some(all_capability_names))?;
1244 let source_names = n.value.into_iter();
1245 let target_names = all_target_capability_names(expose, expose)
1246 .ok_or_else(|| Error::internal("no capability"))?;
1247 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1248 {
1249 let DerivedSourceInfo { source, source_dictionary, availability } =
1250 derive_source_and_availability(
1251 expose.availability.as_ref(),
1252 source.clone(),
1253 source_dictionary.clone(),
1254 expose.source_availability.as_ref(),
1255 all_capability_names,
1256 all_children,
1257 all_collections,
1258 );
1259 out_exposes.push(fdecl::Expose::Protocol(fdecl::ExposeProtocol {
1260 source: Some(source),
1261 source_name: Some(source_name.to_string()),
1262 source_dictionary,
1263 target_name: Some(target_name.to_string()),
1264 target: Some(target.clone()),
1265 availability: Some(availability),
1266 ..Default::default()
1267 }))
1268 }
1269 } else if let Some(n) = expose.directory() {
1270 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1271 let source_names = n.value.into_iter();
1272 let target_names = all_target_capability_names(expose, expose)
1273 .ok_or_else(|| Error::internal("no capability"))?;
1274 let rights = extract_expose_rights(expose)?;
1275 let subdir = extract_expose_subdir(expose);
1276 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1277 {
1278 let DerivedSourceInfo { source, source_dictionary, availability } =
1279 derive_source_and_availability(
1280 expose.availability.as_ref(),
1281 source.clone(),
1282 source_dictionary.clone(),
1283 expose.source_availability.as_ref(),
1284 all_capability_names,
1285 all_children,
1286 all_collections,
1287 );
1288 out_exposes.push(fdecl::Expose::Directory(fdecl::ExposeDirectory {
1289 source: Some(source),
1290 source_name: Some(source_name.to_string()),
1291 source_dictionary,
1292 target_name: Some(target_name.to_string()),
1293 target: Some(target.clone()),
1294 rights,
1295 subdir: subdir.as_ref().map(|s| s.clone().into()),
1296 availability: Some(availability),
1297 ..Default::default()
1298 }))
1299 }
1300 } else if let Some(n) = expose.runner() {
1301 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1302 let source_names = n.value.into_iter();
1303 let target_names = all_target_capability_names(expose, expose)
1304 .ok_or_else(|| Error::internal("no capability"))?;
1305 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1306 {
1307 out_exposes.push(fdecl::Expose::Runner(fdecl::ExposeRunner {
1308 source: Some(source.clone()),
1309 source_name: Some(source_name.to_string()),
1310 source_dictionary: source_dictionary.clone(),
1311 target: Some(target.clone()),
1312 target_name: Some(target_name.to_string()),
1313 ..Default::default()
1314 }))
1315 }
1316 } else if let Some(n) = expose.resolver() {
1317 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1318 let source_names = n.value.into_iter();
1319 let target_names = all_target_capability_names(expose, expose)
1320 .ok_or_else(|| Error::internal("no capability"))?;
1321 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1322 {
1323 out_exposes.push(fdecl::Expose::Resolver(fdecl::ExposeResolver {
1324 source: Some(source.clone()),
1325 source_name: Some(source_name.to_string()),
1326 source_dictionary: source_dictionary.clone(),
1327 target: Some(target.clone()),
1328 target_name: Some(target_name.to_string()),
1329 ..Default::default()
1330 }))
1331 }
1332 } else if let Some(n) = expose.dictionary() {
1333 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1334 let source_names = n.value.into_iter();
1335 let target_names = all_target_capability_names(expose, expose)
1336 .ok_or_else(|| Error::internal("no capability"))?;
1337 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1338 {
1339 let DerivedSourceInfo { source, source_dictionary, availability } =
1340 derive_source_and_availability(
1341 expose.availability.as_ref(),
1342 source.clone(),
1343 source_dictionary.clone(),
1344 expose.source_availability.as_ref(),
1345 all_capability_names,
1346 all_children,
1347 all_collections,
1348 );
1349 out_exposes.push(fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
1350 source: Some(source),
1351 source_name: Some(source_name.to_string()),
1352 source_dictionary,
1353 target_name: Some(target_name.to_string()),
1354 target: Some(target.clone()),
1355 availability: Some(availability),
1356 ..Default::default()
1357 }))
1358 }
1359 } else if let Some(n) = expose.config() {
1360 let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
1361 let source_names = n.value.into_iter();
1362 let target_names = all_target_capability_names(expose, expose)
1363 .ok_or_else(|| Error::internal("no capability"))?;
1364 for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
1365 {
1366 let DerivedSourceInfo { source, source_dictionary, availability } =
1367 derive_source_and_availability(
1368 expose.availability.as_ref(),
1369 source.clone(),
1370 source_dictionary.clone(),
1371 expose.source_availability.as_ref(),
1372 all_capability_names,
1373 all_children,
1374 all_collections,
1375 );
1376 out_exposes.push(fdecl::Expose::Config(fdecl::ExposeConfiguration {
1377 source: Some(source.clone()),
1378 source_name: Some(source_name.to_string()),
1379 source_dictionary,
1380 target: Some(target.clone()),
1381 target_name: Some(target_name.to_string()),
1382 availability: Some(availability),
1383 ..Default::default()
1384 }))
1385 }
1386 } else {
1387 return Err(Error::internal(format!("expose: must specify a known capability")));
1388 }
1389 }
1390 Ok(out_exposes)
1391}
1392
1393impl<T> Into<Vec<T>> for OneOrMany<T> {
1394 fn into(self) -> Vec<T> {
1395 match self {
1396 OneOrMany::One(one) => vec![one],
1397 OneOrMany::Many(many) => many,
1398 }
1399 }
1400}
1401
1402fn annotate_type<T>(val: T) -> T {
1404 val
1405}
1406
1407struct DerivedSourceInfo {
1408 source: fdecl::Ref,
1409 source_dictionary: Option<String>,
1410 availability: fdecl::Availability,
1411}
1412
1413fn derive_source_and_availability_deprecated(
1416 availability: Option<&Availability>,
1417 source: fdecl::Ref,
1418 source_dictionary: Option<String>,
1419 source_availability: Option<&SourceAvailability>,
1420 all_capability_names: &BTreeSet<&BorrowedName>,
1421 all_children: &BTreeSet<&BorrowedName>,
1422 all_collections: &BTreeSet<&BorrowedName>,
1423) -> DerivedSourceInfo {
1424 let availability = availability.map(|a| match a {
1425 Availability::Required => fdecl::Availability::Required,
1426 Availability::Optional => fdecl::Availability::Optional,
1427 Availability::SameAsTarget => fdecl::Availability::SameAsTarget,
1428 Availability::Transitional => fdecl::Availability::Transitional,
1429 });
1430 if source_availability != Some(&SourceAvailability::Unknown) {
1431 return DerivedSourceInfo {
1432 source,
1433 source_dictionary,
1434 availability: availability.unwrap_or(fdecl::Availability::Required),
1435 };
1436 }
1437 match &source {
1438 fdecl::Ref::Child(fdecl::ChildRef { name, .. })
1439 if !all_children.contains(name.as_str()) =>
1440 {
1441 DerivedSourceInfo {
1442 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1443 source_dictionary: None,
1444 availability: availability.unwrap_or(fdecl::Availability::Optional),
1445 }
1446 }
1447 fdecl::Ref::Collection(fdecl::CollectionRef { name, .. })
1448 if !all_collections.contains(name.as_str()) =>
1449 {
1450 DerivedSourceInfo {
1451 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1452 source_dictionary: None,
1453 availability: availability.unwrap_or(fdecl::Availability::Optional),
1454 }
1455 }
1456 fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. })
1457 if !all_capability_names.contains(name.as_str()) =>
1458 {
1459 DerivedSourceInfo {
1460 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1461 source_dictionary: None,
1462 availability: availability.unwrap_or(fdecl::Availability::Optional),
1463 }
1464 }
1465 _ => DerivedSourceInfo {
1466 source,
1467 source_dictionary,
1468 availability: availability.unwrap_or(fdecl::Availability::Required),
1469 },
1470 }
1471}
1472
1473fn derive_source_and_availability(
1476 availability: Option<&ContextSpanned<Availability>>,
1477 source: fdecl::Ref,
1478 source_dictionary: Option<String>,
1479 source_availability: Option<&ContextSpanned<SourceAvailability>>,
1480 all_capability_names: &BTreeSet<Name>,
1481 all_children: &BTreeSet<Name>,
1482 all_collections: &BTreeSet<Name>,
1483) -> DerivedSourceInfo {
1484 let availability = availability.map(|a| match a.value {
1485 Availability::Required => fdecl::Availability::Required,
1486 Availability::Optional => fdecl::Availability::Optional,
1487 Availability::SameAsTarget => fdecl::Availability::SameAsTarget,
1488 Availability::Transitional => fdecl::Availability::Transitional,
1489 });
1490 if source_availability.as_ref().map(|s| s.value.clone()) != Some(SourceAvailability::Unknown) {
1491 return DerivedSourceInfo {
1492 source,
1493 source_dictionary,
1494 availability: availability.unwrap_or(fdecl::Availability::Required),
1495 };
1496 }
1497 match &source {
1498 fdecl::Ref::Child(fdecl::ChildRef { name, .. })
1499 if !all_children.contains(name.as_str()) =>
1500 {
1501 DerivedSourceInfo {
1502 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1503 source_dictionary: None,
1504 availability: availability.unwrap_or(fdecl::Availability::Optional),
1505 }
1506 }
1507 fdecl::Ref::Collection(fdecl::CollectionRef { name, .. })
1508 if !all_collections.contains(name.as_str()) =>
1509 {
1510 DerivedSourceInfo {
1511 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1512 source_dictionary: None,
1513 availability: availability.unwrap_or(fdecl::Availability::Optional),
1514 }
1515 }
1516 fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. })
1517 if !all_capability_names.contains(name.as_str()) =>
1518 {
1519 DerivedSourceInfo {
1520 source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
1521 source_dictionary: None,
1522 availability: availability.unwrap_or(fdecl::Availability::Optional),
1523 }
1524 }
1525 _ => DerivedSourceInfo {
1526 source,
1527 source_dictionary,
1528 availability: availability.unwrap_or(fdecl::Availability::Required),
1529 },
1530 }
1531}
1532
1533fn maybe_generate_direct_offer_from_all_deprecated(
1536 offer_to_all: &Offer,
1537 direct_offers: &[Offer],
1538 target: &BorrowedName,
1539) -> Vec<Offer> {
1540 assert!(offer_to_all.protocol.is_some() || offer_to_all.dictionary.is_some());
1541 let mut returned_offers = vec![];
1542 for mut local_offer in offer_to_all
1543 .protocol
1544 .as_ref()
1545 .unwrap_or(&OneOrMany::Many(vec![]))
1546 .iter()
1547 .map(|individual_protocol| {
1548 let mut local_offer = offer_to_all.clone();
1549 local_offer.protocol = Some(OneOrMany::One(individual_protocol.clone()));
1550 local_offer
1551 })
1552 .chain(
1553 offer_to_all.dictionary.as_ref().unwrap_or(&OneOrMany::Many(vec![])).into_iter().map(
1554 |dictionary| {
1555 let mut local_offer = offer_to_all.clone();
1556 local_offer.dictionary = Some(OneOrMany::One(dictionary.clone()));
1557 local_offer
1558 },
1559 ),
1560 )
1561 {
1562 let disallowed_offer_source = OfferFromRef::Named(target.into());
1563 if direct_offers.iter().all(|direct| {
1564 !offer_to_all_would_duplicate(&local_offer, direct, target).unwrap()
1567 }) && !local_offer.from.iter().any(|from| from == &disallowed_offer_source)
1568 {
1569 local_offer.to = OneOrMany::One(OfferToRef::Named(target.into()));
1570 returned_offers.push(local_offer);
1571 }
1572 }
1573
1574 returned_offers
1575}
1576
1577fn maybe_generate_direct_offer_from_all(
1578 offer_to_all: &ContextSpanned<ContextOffer>,
1579 direct_offers: &[ContextSpanned<ContextOffer>],
1580 target: &BorrowedName,
1581) -> Vec<ContextSpanned<ContextOffer>> {
1582 assert!(offer_to_all.value.protocol.is_some() || offer_to_all.value.dictionary.is_some());
1583 let mut returned_offers = vec![];
1584
1585 let protocol_iter = offer_to_all.value.protocol.as_ref().into_iter().flat_map(|spanned| {
1586 let origin = spanned.origin.clone();
1587 spanned.value.iter().map(move |individual_protocol| {
1588 let mut local_offer_spanned = offer_to_all.clone();
1589
1590 local_offer_spanned.value.protocol = Some(ContextSpanned {
1591 value: OneOrMany::One(individual_protocol.clone()),
1592 origin: origin.clone(),
1593 });
1594 local_offer_spanned
1595 })
1596 });
1597
1598 let dict_iter = offer_to_all.value.dictionary.as_ref().into_iter().flat_map(|spanned| {
1599 let origin = spanned.origin.clone();
1600 spanned.value.iter().map(move |dictionary| {
1601 let mut local_offer_spanned = offer_to_all.clone();
1602
1603 local_offer_spanned.value.dictionary = Some(ContextSpanned {
1604 value: OneOrMany::One(dictionary.clone()),
1605 origin: origin.clone(),
1606 });
1607 local_offer_spanned
1608 })
1609 });
1610
1611 for mut local_offer_spanned in protocol_iter.chain(dict_iter) {
1612 let disallowed_offer_source = OfferFromRef::Named(target.into());
1613
1614 if direct_offers.iter().all(|direct| {
1615 !offer_to_all_would_duplicate_context(&local_offer_spanned, direct, target).unwrap()
1616 }) && !local_offer_spanned
1617 .value
1618 .from
1619 .value
1620 .iter()
1621 .any(|from| from == &disallowed_offer_source)
1622 {
1623 local_offer_spanned.value.to = ContextSpanned {
1624 value: OneOrMany::One(OfferToRef::Named(target.into())),
1625 origin: local_offer_spanned.origin.clone(),
1626 };
1627 returned_offers.push(local_offer_spanned);
1628 }
1629 }
1630
1631 returned_offers
1632}
1633
1634fn expand_offer_to_all_deprecated(
1635 offers_in: &Vec<Offer>,
1636 children: &BTreeSet<&BorrowedName>,
1637 collections: &BTreeSet<&BorrowedName>,
1638) -> Vec<Offer> {
1639 let offers_to_all =
1640 offers_in.iter().filter(|offer| matches!(offer.to, OneOrMany::One(OfferToRef::All)));
1641
1642 let mut direct_offers = offers_in
1643 .iter()
1644 .filter(|o| !matches!(o.to, OneOrMany::One(OfferToRef::All)))
1645 .map(Offer::clone)
1646 .collect::<Vec<Offer>>();
1647
1648 for offer_to_all in offers_to_all {
1649 for target in children.iter().chain(collections.iter()) {
1650 let offers = maybe_generate_direct_offer_from_all_deprecated(
1651 offer_to_all,
1652 &direct_offers,
1653 target,
1654 );
1655 for offer in offers {
1656 direct_offers.push(offer);
1657 }
1658 }
1659 }
1660
1661 direct_offers
1662}
1663
1664fn expand_offer_to_all(
1665 offers_in: &Vec<ContextSpanned<ContextOffer>>,
1666 children: &BTreeSet<Name>,
1667 collections: &BTreeSet<Name>,
1668) -> Vec<ContextSpanned<ContextOffer>> {
1669 let offers_to_all = offers_in
1670 .iter()
1671 .filter(|offer| matches!(offer.value.to.value, OneOrMany::One(OfferToRef::All)));
1672
1673 let mut direct_offers = offers_in
1674 .iter()
1675 .filter(|o| !matches!(o.value.to.value, OneOrMany::One(OfferToRef::All)))
1676 .cloned()
1677 .collect::<Vec<ContextSpanned<ContextOffer>>>();
1678
1679 for offer_to_all in offers_to_all {
1680 for target in children.iter().chain(collections.iter()) {
1681 let offers = maybe_generate_direct_offer_from_all(offer_to_all, &direct_offers, target);
1682 for offer in offers {
1683 direct_offers.push(offer);
1684 }
1685 }
1686 }
1687
1688 direct_offers
1689}
1690
1691fn translate_offer_deprecated(
1693 options: &CompileOptions<'_>,
1694 offer_in: &Vec<Offer>,
1695 all_capability_names: &BTreeSet<&BorrowedName>,
1696 all_children: &BTreeSet<&BorrowedName>,
1697 all_collections: &BTreeSet<&BorrowedName>,
1698) -> Result<Vec<fdecl::Offer>, Error> {
1699 let mut out_offers = vec![];
1700 let expanded_offers = expand_offer_to_all_deprecated(offer_in, all_children, all_collections);
1701 for offer in &expanded_offers {
1702 if let Some(n) = offer.service() {
1703 let entries = extract_offer_sources_and_targets_deprecated(
1704 options,
1705 offer,
1706 n,
1707 all_capability_names,
1708 all_children,
1709 all_collections,
1710 )?;
1711 for (source, source_dictionary, source_name, target, target_name) in entries {
1712 let DerivedSourceInfo { source, source_dictionary, availability } =
1713 derive_source_and_availability_deprecated(
1714 offer.availability.as_ref(),
1715 source,
1716 source_dictionary,
1717 offer.source_availability.as_ref(),
1718 all_capability_names,
1719 all_children,
1720 all_collections,
1721 );
1722 out_offers.push(fdecl::Offer::Service(fdecl::OfferService {
1723 source: Some(source),
1724 source_name: Some(source_name.to_string()),
1725 source_dictionary,
1726 target: Some(target),
1727 target_name: Some(target_name.to_string()),
1728 availability: Some(availability),
1729 #[cfg(fuchsia_api_level_at_least = "HEAD")]
1730 dependency_type: Some(
1731 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1732 ),
1733 ..Default::default()
1734 }));
1735 }
1736 } else if let Some(n) = offer.protocol() {
1737 let entries = extract_offer_sources_and_targets_deprecated(
1738 options,
1739 offer,
1740 n,
1741 all_capability_names,
1742 all_children,
1743 all_collections,
1744 )?;
1745 for (source, source_dictionary, source_name, target, target_name) in entries {
1746 let DerivedSourceInfo { source, source_dictionary, availability } =
1747 derive_source_and_availability_deprecated(
1748 offer.availability.as_ref(),
1749 source,
1750 source_dictionary,
1751 offer.source_availability.as_ref(),
1752 all_capability_names,
1753 all_children,
1754 all_collections,
1755 );
1756 out_offers.push(fdecl::Offer::Protocol(fdecl::OfferProtocol {
1757 source: Some(source),
1758 source_name: Some(source_name.to_string()),
1759 source_dictionary,
1760 target: Some(target),
1761 target_name: Some(target_name.to_string()),
1762 dependency_type: Some(
1763 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1764 ),
1765 availability: Some(availability),
1766 ..Default::default()
1767 }));
1768 }
1769 } else if let Some(n) = offer.directory() {
1770 let entries = extract_offer_sources_and_targets_deprecated(
1771 options,
1772 offer,
1773 n,
1774 all_capability_names,
1775 all_children,
1776 all_collections,
1777 )?;
1778 for (source, source_dictionary, source_name, target, target_name) in entries {
1779 let DerivedSourceInfo { source, source_dictionary, availability } =
1780 derive_source_and_availability_deprecated(
1781 offer.availability.as_ref(),
1782 source,
1783 source_dictionary,
1784 offer.source_availability.as_ref(),
1785 all_capability_names,
1786 all_children,
1787 all_collections,
1788 );
1789 out_offers.push(fdecl::Offer::Directory(fdecl::OfferDirectory {
1790 source: Some(source),
1791 source_name: Some(source_name.to_string()),
1792 source_dictionary,
1793 target: Some(target),
1794 target_name: Some(target_name.to_string()),
1795 rights: extract_offer_rights_deprecated(offer)?,
1796 subdir: extract_offer_subdir_deprecated(offer).map(|s| s.into()),
1797 dependency_type: Some(
1798 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1799 ),
1800 availability: Some(availability),
1801 ..Default::default()
1802 }));
1803 }
1804 } else if let Some(n) = offer.storage() {
1805 let entries = extract_offer_sources_and_targets_deprecated(
1806 options,
1807 offer,
1808 n,
1809 all_capability_names,
1810 all_children,
1811 all_collections,
1812 )?;
1813 for (source, source_dictionary, source_name, target, target_name) in entries {
1814 let DerivedSourceInfo { source, source_dictionary: _, availability } =
1815 derive_source_and_availability_deprecated(
1816 offer.availability.as_ref(),
1817 source,
1818 source_dictionary,
1819 offer.source_availability.as_ref(),
1820 all_capability_names,
1821 all_children,
1822 all_collections,
1823 );
1824 out_offers.push(fdecl::Offer::Storage(fdecl::OfferStorage {
1825 source: Some(source),
1826 source_name: Some(source_name.to_string()),
1827 target: Some(target),
1828 target_name: Some(target_name.to_string()),
1829 availability: Some(availability),
1830 ..Default::default()
1831 }));
1832 }
1833 } else if let Some(n) = offer.runner() {
1834 let entries = extract_offer_sources_and_targets_deprecated(
1835 options,
1836 offer,
1837 n,
1838 all_capability_names,
1839 all_children,
1840 all_collections,
1841 )?;
1842 for (source, source_dictionary, source_name, target, target_name) in entries {
1843 out_offers.push(fdecl::Offer::Runner(fdecl::OfferRunner {
1844 source: Some(source),
1845 source_name: Some(source_name.to_string()),
1846 source_dictionary,
1847 target: Some(target),
1848 target_name: Some(target_name.to_string()),
1849 ..Default::default()
1850 }));
1851 }
1852 } else if let Some(n) = offer.resolver() {
1853 let entries = extract_offer_sources_and_targets_deprecated(
1854 options,
1855 offer,
1856 n,
1857 all_capability_names,
1858 all_children,
1859 all_collections,
1860 )?;
1861 for (source, source_dictionary, source_name, target, target_name) in entries {
1862 out_offers.push(fdecl::Offer::Resolver(fdecl::OfferResolver {
1863 source: Some(source),
1864 source_name: Some(source_name.to_string()),
1865 source_dictionary,
1866 target: Some(target),
1867 target_name: Some(target_name.to_string()),
1868 ..Default::default()
1869 }));
1870 }
1871 } else if let Some(n) = offer.event_stream() {
1872 let entries = extract_offer_sources_and_targets_deprecated(
1873 options,
1874 offer,
1875 n,
1876 all_capability_names,
1877 all_children,
1878 all_collections,
1879 )?;
1880 for (source, source_dictionary, source_name, target, target_name) in entries {
1881 let DerivedSourceInfo { source, source_dictionary: _, availability } =
1882 derive_source_and_availability_deprecated(
1883 offer.availability.as_ref(),
1884 source,
1885 source_dictionary,
1886 offer.source_availability.as_ref(),
1887 all_capability_names,
1888 all_children,
1889 all_collections,
1890 );
1891 let scopes = match offer.scope.clone() {
1892 Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
1893 None => None,
1894 };
1895 out_offers.push(fdecl::Offer::EventStream(fdecl::OfferEventStream {
1896 source: Some(source),
1897 source_name: Some(source_name.to_string()),
1898 target: Some(target),
1899 target_name: Some(target_name.to_string()),
1900 scope: match scopes {
1901 Some(values) => {
1902 let mut output = vec![];
1903 for value in &values {
1904 static EMPTY_SET: BTreeSet<&BorrowedName> = BTreeSet::new();
1905 if let Some(target) = translate_target_ref_deprecated(
1906 options,
1907 value.into(),
1908 &all_children,
1909 &all_collections,
1910 &EMPTY_SET,
1911 offer.target_availability.as_ref(),
1912 )? {
1913 output.push(target);
1914 }
1915 }
1916 Some(output)
1917 }
1918 None => None,
1919 },
1920 availability: Some(availability),
1921 ..Default::default()
1922 }));
1923 }
1924 } else if let Some(n) = offer.dictionary() {
1925 let entries = extract_offer_sources_and_targets_deprecated(
1926 options,
1927 offer,
1928 n,
1929 all_capability_names,
1930 all_children,
1931 all_collections,
1932 )?;
1933 for (source, source_dictionary, source_name, target, target_name) in entries {
1934 let DerivedSourceInfo { source, source_dictionary, availability } =
1935 derive_source_and_availability_deprecated(
1936 offer.availability.as_ref(),
1937 source,
1938 source_dictionary,
1939 offer.source_availability.as_ref(),
1940 all_capability_names,
1941 all_children,
1942 all_collections,
1943 );
1944 out_offers.push(fdecl::Offer::Dictionary(fdecl::OfferDictionary {
1945 source: Some(source),
1946 source_name: Some(source_name.to_string()),
1947 source_dictionary,
1948 target: Some(target),
1949 target_name: Some(target_name.to_string()),
1950 dependency_type: Some(
1951 offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
1952 ),
1953 availability: Some(availability),
1954 ..Default::default()
1955 }));
1956 }
1957 } else if let Some(n) = offer.config() {
1958 let entries = extract_offer_sources_and_targets_deprecated(
1959 options,
1960 offer,
1961 n,
1962 all_capability_names,
1963 all_children,
1964 all_collections,
1965 )?;
1966 for (source, source_dictionary, source_name, target, target_name) in entries {
1967 let DerivedSourceInfo { source, source_dictionary, availability } =
1968 derive_source_and_availability_deprecated(
1969 offer.availability.as_ref(),
1970 source,
1971 source_dictionary,
1972 offer.source_availability.as_ref(),
1973 all_capability_names,
1974 all_children,
1975 all_collections,
1976 );
1977 out_offers.push(fdecl::Offer::Config(fdecl::OfferConfiguration {
1978 source: Some(source),
1979 source_name: Some(source_name.to_string()),
1980 target: Some(target),
1981 target_name: Some(target_name.to_string()),
1982 availability: Some(availability),
1983 source_dictionary,
1984 ..Default::default()
1985 }));
1986 }
1987 } else {
1988 return Err(Error::internal(format!("no capability")));
1989 }
1990 }
1991 Ok(out_offers)
1992}
1993
1994fn translate_offer(
1996 options: &CompileOptions<'_>,
1997 offer_in: &Vec<ContextSpanned<ContextOffer>>,
1998 all_capability_names: &BTreeSet<Name>,
1999 all_children: &BTreeSet<Name>,
2000 all_collections: &BTreeSet<Name>,
2001) -> Result<Vec<fdecl::Offer>, Error> {
2002 let mut out_offers = vec![];
2003 let expanded_offers = expand_offer_to_all(offer_in, all_children, all_collections);
2004 for offer_spanned in &expanded_offers {
2005 let offer = &offer_spanned.value;
2006 if let Some(n) = offer.service() {
2007 let entries = extract_offer_sources_and_targets(
2008 options,
2009 offer,
2010 n.value,
2011 all_capability_names,
2012 all_children,
2013 all_collections,
2014 )?;
2015 for (source, source_dictionary, source_name, target, target_name) in entries {
2016 let DerivedSourceInfo { source, source_dictionary, availability } =
2017 derive_source_and_availability(
2018 offer.availability.as_ref(),
2019 source,
2020 source_dictionary,
2021 offer.source_availability.as_ref(),
2022 all_capability_names,
2023 all_children,
2024 all_collections,
2025 );
2026 out_offers.push(fdecl::Offer::Service(fdecl::OfferService {
2027 source: Some(source),
2028 source_name: Some(source_name.to_string()),
2029 source_dictionary,
2030 target: Some(target),
2031 target_name: Some(target_name.to_string()),
2032 availability: Some(availability),
2033 #[cfg(fuchsia_api_level_at_least = "HEAD")]
2034 dependency_type: Some(
2035 offer
2036 .dependency
2037 .clone()
2038 .map(|s| s.value)
2039 .unwrap_or(cm::DependencyType::Strong)
2040 .into(),
2041 ),
2042 ..Default::default()
2043 }));
2044 }
2045 } else if let Some(n) = offer.protocol() {
2046 let entries = extract_offer_sources_and_targets(
2047 options,
2048 offer,
2049 n.value,
2050 all_capability_names,
2051 all_children,
2052 all_collections,
2053 )?;
2054 for (source, source_dictionary, source_name, target, target_name) in entries {
2055 let DerivedSourceInfo { source, source_dictionary, availability } =
2056 derive_source_and_availability(
2057 offer.availability.as_ref(),
2058 source,
2059 source_dictionary,
2060 offer.source_availability.as_ref(),
2061 all_capability_names,
2062 all_children,
2063 all_collections,
2064 );
2065 out_offers.push(fdecl::Offer::Protocol(fdecl::OfferProtocol {
2066 source: Some(source),
2067 source_name: Some(source_name.to_string()),
2068 source_dictionary,
2069 target: Some(target),
2070 target_name: Some(target_name.to_string()),
2071 dependency_type: Some(
2072 offer
2073 .dependency
2074 .clone()
2075 .map(|s| s.value)
2076 .unwrap_or(cm::DependencyType::Strong)
2077 .into(),
2078 ),
2079 availability: Some(availability),
2080 ..Default::default()
2081 }));
2082 }
2083 } else if let Some(n) = offer.directory() {
2084 let entries = extract_offer_sources_and_targets(
2085 options,
2086 offer,
2087 n.value,
2088 all_capability_names,
2089 all_children,
2090 all_collections,
2091 )?;
2092 for (source, source_dictionary, source_name, target, target_name) in entries {
2093 let DerivedSourceInfo { source, source_dictionary, availability } =
2094 derive_source_and_availability(
2095 offer.availability.as_ref(),
2096 source,
2097 source_dictionary,
2098 offer.source_availability.as_ref(),
2099 all_capability_names,
2100 all_children,
2101 all_collections,
2102 );
2103 out_offers.push(fdecl::Offer::Directory(fdecl::OfferDirectory {
2104 source: Some(source),
2105 source_name: Some(source_name.to_string()),
2106 source_dictionary,
2107 target: Some(target),
2108 target_name: Some(target_name.to_string()),
2109 rights: extract_offer_rights(&offer)?,
2110 subdir: extract_offer_subdir(&offer).map(|s| s.into()),
2111 dependency_type: Some(
2112 offer
2113 .dependency
2114 .clone()
2115 .map(|s| s.value)
2116 .unwrap_or(cm::DependencyType::Strong)
2117 .into(),
2118 ),
2119 availability: Some(availability),
2120 ..Default::default()
2121 }));
2122 }
2123 } else if let Some(n) = offer.storage() {
2124 let entries = extract_offer_sources_and_targets(
2125 options,
2126 offer,
2127 n.value,
2128 all_capability_names,
2129 all_children,
2130 all_collections,
2131 )?;
2132 for (source, source_dictionary, source_name, target, target_name) in entries {
2133 let DerivedSourceInfo { source, source_dictionary: _, availability } =
2134 derive_source_and_availability(
2135 offer.availability.as_ref(),
2136 source,
2137 source_dictionary,
2138 offer.source_availability.as_ref(),
2139 all_capability_names,
2140 all_children,
2141 all_collections,
2142 );
2143 out_offers.push(fdecl::Offer::Storage(fdecl::OfferStorage {
2144 source: Some(source),
2145 source_name: Some(source_name.to_string()),
2146 target: Some(target),
2147 target_name: Some(target_name.to_string()),
2148 availability: Some(availability),
2149 ..Default::default()
2150 }));
2151 }
2152 } else if let Some(n) = offer.runner() {
2153 let entries = extract_offer_sources_and_targets(
2154 options,
2155 offer,
2156 n.value,
2157 all_capability_names,
2158 all_children,
2159 all_collections,
2160 )?;
2161 for (source, source_dictionary, source_name, target, target_name) in entries {
2162 out_offers.push(fdecl::Offer::Runner(fdecl::OfferRunner {
2163 source: Some(source),
2164 source_name: Some(source_name.to_string()),
2165 source_dictionary,
2166 target: Some(target),
2167 target_name: Some(target_name.to_string()),
2168 ..Default::default()
2169 }));
2170 }
2171 } else if let Some(n) = offer.resolver() {
2172 let entries = extract_offer_sources_and_targets(
2173 options,
2174 offer,
2175 n.value,
2176 all_capability_names,
2177 all_children,
2178 all_collections,
2179 )?;
2180 for (source, source_dictionary, source_name, target, target_name) in entries {
2181 out_offers.push(fdecl::Offer::Resolver(fdecl::OfferResolver {
2182 source: Some(source),
2183 source_name: Some(source_name.to_string()),
2184 source_dictionary,
2185 target: Some(target),
2186 target_name: Some(target_name.to_string()),
2187 ..Default::default()
2188 }));
2189 }
2190 } else if let Some(n) = offer.event_stream() {
2191 let entries = extract_offer_sources_and_targets(
2192 options,
2193 offer,
2194 n.value,
2195 all_capability_names,
2196 all_children,
2197 all_collections,
2198 )?;
2199 for (source, source_dictionary, source_name, target, target_name) in entries {
2200 let DerivedSourceInfo { source, source_dictionary: _, availability } =
2201 derive_source_and_availability(
2202 offer.availability.as_ref(),
2203 source,
2204 source_dictionary,
2205 offer.source_availability.as_ref(),
2206 all_capability_names,
2207 all_children,
2208 all_collections,
2209 );
2210 let scopes = match offer.scope.clone() {
2211 Some(value) => Some(annotate_type::<Vec<EventScope>>(value.value.into())),
2212 None => None,
2213 };
2214 out_offers.push(fdecl::Offer::EventStream(fdecl::OfferEventStream {
2215 source: Some(source),
2216 source_name: Some(source_name.to_string()),
2217 target: Some(target),
2218 target_name: Some(target_name.to_string()),
2219 scope: match scopes {
2220 Some(values) => {
2221 let mut output = vec![];
2222 for value in &values {
2223 if let Some(target) = translate_target_ref(
2224 options,
2225 value.into(),
2226 &all_children,
2227 &all_collections,
2228 &BTreeSet::new(),
2229 offer.target_availability.clone().map(|s| s.value).as_ref(),
2230 )? {
2231 output.push(target);
2232 }
2233 }
2234 Some(output)
2235 }
2236 None => None,
2237 },
2238 availability: Some(availability),
2239 ..Default::default()
2240 }));
2241 }
2242 } else if let Some(n) = offer.dictionary() {
2243 let entries = extract_offer_sources_and_targets(
2244 options,
2245 offer,
2246 n.value,
2247 all_capability_names,
2248 all_children,
2249 all_collections,
2250 )?;
2251 for (source, source_dictionary, source_name, target, target_name) in entries {
2252 let DerivedSourceInfo { source, source_dictionary, availability } =
2253 derive_source_and_availability(
2254 offer.availability.as_ref(),
2255 source,
2256 source_dictionary,
2257 offer.source_availability.as_ref(),
2258 all_capability_names,
2259 all_children,
2260 all_collections,
2261 );
2262 out_offers.push(fdecl::Offer::Dictionary(fdecl::OfferDictionary {
2263 source: Some(source),
2264 source_name: Some(source_name.to_string()),
2265 source_dictionary,
2266 target: Some(target),
2267 target_name: Some(target_name.to_string()),
2268 dependency_type: Some(
2269 offer
2270 .dependency
2271 .clone()
2272 .map(|s| s.value)
2273 .unwrap_or(cm::DependencyType::Strong)
2274 .into(),
2275 ),
2276 availability: Some(availability),
2277 ..Default::default()
2278 }));
2279 }
2280 } else if let Some(n) = offer.config() {
2281 let entries = extract_offer_sources_and_targets(
2282 options,
2283 offer,
2284 n.value,
2285 all_capability_names,
2286 all_children,
2287 all_collections,
2288 )?;
2289 for (source, source_dictionary, source_name, target, target_name) in entries {
2290 let DerivedSourceInfo { source, source_dictionary, availability } =
2291 derive_source_and_availability(
2292 offer.availability.as_ref(),
2293 source,
2294 source_dictionary,
2295 offer.source_availability.as_ref(),
2296 all_capability_names,
2297 all_children,
2298 all_collections,
2299 );
2300 out_offers.push(fdecl::Offer::Config(fdecl::OfferConfiguration {
2301 source: Some(source),
2302 source_name: Some(source_name.to_string()),
2303 target: Some(target),
2304 target_name: Some(target_name.to_string()),
2305 availability: Some(availability),
2306 source_dictionary,
2307 ..Default::default()
2308 }));
2309 }
2310 } else {
2311 return Err(Error::internal(format!("no capability")));
2312 }
2313 }
2314 Ok(out_offers)
2315}
2316
2317fn translate_children_deprecated(children_in: &Vec<Child>) -> Vec<fdecl::Child> {
2318 let mut out_children = vec![];
2319 for child in children_in.iter() {
2320 out_children.push(fdecl::Child {
2321 name: Some(child.name.clone().into()),
2322 url: Some(child.url.clone().into()),
2323 startup: Some(child.startup.clone().into()),
2324 environment: extract_environment_ref_deprecated(child.environment.as_ref())
2325 .map(|e| e.into()),
2326 on_terminate: child.on_terminate.as_ref().map(|r| r.clone().into()),
2327 ..Default::default()
2328 });
2329 }
2330 out_children
2331}
2332
2333fn translate_children(children_in: &Vec<ContextSpanned<ContextChild>>) -> Vec<fdecl::Child> {
2334 let mut out_children = vec![];
2335 for child_raw in children_in.iter() {
2336 let child = &child_raw.value;
2337 out_children.push(fdecl::Child {
2338 name: Some(child.name.value.clone().into()),
2339 url: Some(child.url.value.clone().into()),
2340 startup: Some(child.startup.value.clone().into()),
2341 environment: extract_environment_ref(child.environment.as_ref()).map(|e| e.into()),
2342 on_terminate: child.on_terminate.as_ref().map(|r| r.value.clone().into()),
2343 ..Default::default()
2344 });
2345 }
2346 out_children
2347}
2348
2349fn translate_collections_deprecated(collections_in: &Vec<Collection>) -> Vec<fdecl::Collection> {
2350 let mut out_collections = vec![];
2351 for collection in collections_in.iter() {
2352 out_collections.push(fdecl::Collection {
2353 name: Some(collection.name.clone().into()),
2354 durability: Some(collection.durability.clone().into()),
2355 environment: extract_environment_ref_deprecated(collection.environment.as_ref())
2356 .map(|e| e.into()),
2357 allowed_offers: collection.allowed_offers.clone().map(|a| a.into()),
2358 allow_long_names: collection.allow_long_names.clone(),
2359 persistent_storage: collection.persistent_storage.clone(),
2360 ..Default::default()
2361 });
2362 }
2363 out_collections
2364}
2365
2366fn translate_collections(
2367 collections_in: &Vec<ContextSpanned<ContextCollection>>,
2368) -> Vec<fdecl::Collection> {
2369 let mut out_collections = vec![];
2370 for collection_raw in collections_in.iter() {
2371 let collection = &collection_raw.value;
2372 out_collections.push(fdecl::Collection {
2373 name: Some(collection.name.value.clone().into()),
2374 durability: Some(collection.durability.value.clone().into()),
2375 environment: extract_environment_ref(collection.environment.as_ref()).map(|e| e.into()),
2376 allowed_offers: collection.allowed_offers.as_ref().map(|a| a.value.clone().into()),
2377 allow_long_names: collection.allow_long_names.as_ref().map(|a| a.value.into()),
2378 persistent_storage: collection.persistent_storage.as_ref().map(|a| a.value.into()),
2379 ..Default::default()
2380 });
2381 }
2382 out_collections
2383}
2384
2385fn translate_nested_value_type(nested_type: &ConfigNestedValueType) -> fdecl::ConfigType {
2387 let layout = match nested_type {
2388 ConfigNestedValueType::Bool {} => fdecl::ConfigTypeLayout::Bool,
2389 ConfigNestedValueType::Uint8 {} => fdecl::ConfigTypeLayout::Uint8,
2390 ConfigNestedValueType::Uint16 {} => fdecl::ConfigTypeLayout::Uint16,
2391 ConfigNestedValueType::Uint32 {} => fdecl::ConfigTypeLayout::Uint32,
2392 ConfigNestedValueType::Uint64 {} => fdecl::ConfigTypeLayout::Uint64,
2393 ConfigNestedValueType::Int8 {} => fdecl::ConfigTypeLayout::Int8,
2394 ConfigNestedValueType::Int16 {} => fdecl::ConfigTypeLayout::Int16,
2395 ConfigNestedValueType::Int32 {} => fdecl::ConfigTypeLayout::Int32,
2396 ConfigNestedValueType::Int64 {} => fdecl::ConfigTypeLayout::Int64,
2397 ConfigNestedValueType::String { .. } => fdecl::ConfigTypeLayout::String,
2398 };
2399 let constraints = match nested_type {
2400 ConfigNestedValueType::String { max_size } => {
2401 vec![fdecl::LayoutConstraint::MaxSize(max_size.get())]
2402 }
2403 _ => vec![],
2404 };
2405 fdecl::ConfigType {
2406 layout,
2407 constraints,
2408 parameters: Some(vec![]),
2412 }
2413}
2414
2415fn translate_value_type(
2417 value_type: &ConfigValueType,
2418) -> (fdecl::ConfigType, fdecl::ConfigMutability) {
2419 let (layout, source_mutability) = match value_type {
2420 ConfigValueType::Bool { mutability } => (fdecl::ConfigTypeLayout::Bool, mutability),
2421 ConfigValueType::Uint8 { mutability } => (fdecl::ConfigTypeLayout::Uint8, mutability),
2422 ConfigValueType::Uint16 { mutability } => (fdecl::ConfigTypeLayout::Uint16, mutability),
2423 ConfigValueType::Uint32 { mutability } => (fdecl::ConfigTypeLayout::Uint32, mutability),
2424 ConfigValueType::Uint64 { mutability } => (fdecl::ConfigTypeLayout::Uint64, mutability),
2425 ConfigValueType::Int8 { mutability } => (fdecl::ConfigTypeLayout::Int8, mutability),
2426 ConfigValueType::Int16 { mutability } => (fdecl::ConfigTypeLayout::Int16, mutability),
2427 ConfigValueType::Int32 { mutability } => (fdecl::ConfigTypeLayout::Int32, mutability),
2428 ConfigValueType::Int64 { mutability } => (fdecl::ConfigTypeLayout::Int64, mutability),
2429 ConfigValueType::String { mutability, .. } => (fdecl::ConfigTypeLayout::String, mutability),
2430 ConfigValueType::Vector { mutability, .. } => (fdecl::ConfigTypeLayout::Vector, mutability),
2431 };
2432 let (constraints, parameters) = match value_type {
2433 ConfigValueType::String { max_size, .. } => {
2434 (vec![fdecl::LayoutConstraint::MaxSize(max_size.get())], vec![])
2435 }
2436 ConfigValueType::Vector { max_count, element, .. } => {
2437 let nested_type = translate_nested_value_type(element);
2438 (
2439 vec![fdecl::LayoutConstraint::MaxSize(max_count.get())],
2440 vec![fdecl::LayoutParameter::NestedType(nested_type)],
2441 )
2442 }
2443 _ => (vec![], vec![]),
2444 };
2445 let mut mutability = fdecl::ConfigMutability::empty();
2446 if let Some(source_mutability) = source_mutability {
2447 for source in source_mutability {
2448 match source {
2449 ConfigRuntimeSource::Parent => mutability |= fdecl::ConfigMutability::PARENT,
2450 }
2451 }
2452 }
2453 (
2454 fdecl::ConfigType {
2455 layout,
2456 constraints,
2457 parameters: Some(parameters),
2461 },
2462 mutability,
2463 )
2464}
2465
2466fn translate_config_deprecated(
2469 fields: &Option<BTreeMap<ConfigKey, ConfigValueType>>,
2470 uses: &Option<Vec<Use>>,
2471 package_path: &Option<String>,
2472) -> Result<Option<fdecl::ConfigSchema>, Error> {
2473 let mut use_fields: BTreeMap<ConfigKey, ConfigValueType> = uses
2474 .iter()
2475 .flatten()
2476 .map(|u| {
2477 if u.config.is_none() {
2478 return None;
2479 }
2480 let key = ConfigKey(u.key.clone().expect("key should be set").into());
2481 let config_type =
2482 validate::use_config_to_value_type(u).expect("config type should be valid");
2483 Some((key, config_type))
2484 })
2485 .flatten()
2486 .collect();
2487 for (key, value) in fields.iter().flatten() {
2488 if use_fields.contains_key(key) {
2489 if use_fields.get(key) != Some(&value) {
2490 return Err(Error::validate(format!(
2491 "Config error: `use` and `config` block contain key '{}' with different types",
2492 key
2493 )));
2494 }
2495 }
2496 use_fields.insert(key.clone(), value.clone());
2497 }
2498
2499 if use_fields.is_empty() {
2500 return Ok(None);
2501 }
2502
2503 let source = match fields.as_ref().map_or(true, |f| f.is_empty()) {
2504 true => fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default()),
2506 _ => {
2508 let Some(package_path) = package_path.as_ref() else {
2509 return Err(Error::invalid_args(
2510 "can't translate config: no package path for value file",
2511 ));
2512 };
2513 fdecl::ConfigValueSource::PackagePath(package_path.to_owned())
2514 }
2515 };
2516
2517 let mut fidl_fields = vec![];
2518
2519 let mut hasher = Sha256::new();
2521
2522 for (key, value) in &use_fields {
2523 let (type_, mutability) = translate_value_type(value);
2524
2525 fidl_fields.push(fdecl::ConfigField {
2526 key: Some(key.to_string()),
2527 type_: Some(type_),
2528 mutability: Some(mutability),
2529 ..Default::default()
2530 });
2531
2532 hasher.update(key.as_str());
2533
2534 value.update_digest(&mut hasher);
2535 }
2536
2537 let hash = hasher.finalize();
2538 let checksum = fdecl::ConfigChecksum::Sha256(*hash.as_ref());
2539
2540 Ok(Some(fdecl::ConfigSchema {
2541 fields: Some(fidl_fields),
2542 checksum: Some(checksum),
2543 value_source: Some(source),
2544 ..Default::default()
2545 }))
2546}
2547
2548fn translate_config(
2551 fields: &Option<BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>>>,
2552 uses: &Option<Vec<ContextSpanned<ContextUse>>>,
2553 package_path: &Option<String>,
2554) -> Result<Option<fdecl::ConfigSchema>, Error> {
2555 let mut use_fields: BTreeMap<ConfigKey, ContextSpanned<ConfigValueType>> = uses
2556 .iter()
2557 .flatten()
2558 .filter_map(|u| {
2559 if u.value.config.is_none() {
2560 return None;
2561 }
2562 let key = ConfigKey(u.value.key.clone().expect("key should be set").value.into());
2563
2564 let config_type_raw = validate::use_config_to_value_type_context(&u.value)
2565 .expect("config type should be valid");
2566
2567 let config_type = ContextSpanned { value: config_type_raw, origin: u.origin.clone() };
2568
2569 Some((key, config_type))
2570 })
2571 .collect();
2572
2573 for (key, value) in fields.iter().flatten() {
2574 if use_fields.contains_key(key) {
2575 if use_fields.get(key).map(|v| &v.value) != Some(&value.value) {
2576 return Err(Error::validate_context(
2577 format!(
2578 "Config error: `use` and `config` block contain key '{}' with different types",
2579 key
2580 ),
2581 Some(value.origin.clone()),
2582 ));
2583 }
2584 }
2585 use_fields.insert(key.clone(), value.clone());
2586 }
2587
2588 if use_fields.is_empty() {
2589 return Ok(None);
2590 }
2591
2592 let source = match fields.as_ref().map_or(true, |f| f.is_empty()) {
2593 true => fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default()),
2594 _ => {
2595 let Some(package_path) = package_path.as_ref() else {
2596 return Err(Error::invalid_args(
2597 "can't translate config: no package path for value file",
2598 ));
2599 };
2600 fdecl::ConfigValueSource::PackagePath(package_path.to_owned())
2601 }
2602 };
2603
2604 let mut fidl_fields = vec![];
2605 let mut hasher = Sha256::new();
2606
2607 for (key, value) in &use_fields {
2608 let (type_, mutability) = translate_value_type(&value.value);
2609
2610 fidl_fields.push(fdecl::ConfigField {
2611 key: Some(key.to_string()),
2612 type_: Some(type_),
2613 mutability: Some(mutability),
2614 ..Default::default()
2615 });
2616
2617 hasher.update(key.as_str());
2618 value.value.update_digest(&mut hasher);
2619 }
2620
2621 let hash = hasher.finalize();
2622 let checksum = fdecl::ConfigChecksum::Sha256(*hash.as_ref());
2623
2624 Ok(Some(fdecl::ConfigSchema {
2625 fields: Some(fidl_fields),
2626 checksum: Some(checksum),
2627 value_source: Some(source),
2628 ..Default::default()
2629 }))
2630}
2631
2632fn translate_environments_deprecated(
2633 options: &CompileOptions<'_>,
2634 envs_in: &Vec<Environment>,
2635 all_capability_names: &BTreeSet<&BorrowedName>,
2636) -> Result<Vec<fdecl::Environment>, Error> {
2637 envs_in
2638 .iter()
2639 .map(|env| {
2640 Ok(fdecl::Environment {
2641 name: Some(env.name.clone().into()),
2642 extends: match env.extends {
2643 Some(EnvironmentExtends::Realm) => Some(fdecl::EnvironmentExtends::Realm),
2644 Some(EnvironmentExtends::None) => Some(fdecl::EnvironmentExtends::None),
2645 None => Some(fdecl::EnvironmentExtends::None),
2646 },
2647 runners: env
2648 .runners
2649 .as_ref()
2650 .map(|runners| {
2651 runners
2652 .iter()
2653 .map(|r| translate_runner_registration_deprecated(options, r))
2654 .collect::<Result<Vec<_>, Error>>()
2655 })
2656 .transpose()?,
2657 resolvers: env
2658 .resolvers
2659 .as_ref()
2660 .map(|resolvers| {
2661 resolvers
2662 .iter()
2663 .map(|r| translate_resolver_registration_deprecated(options, r))
2664 .collect::<Result<Vec<_>, Error>>()
2665 })
2666 .transpose()?,
2667 debug_capabilities: env
2668 .debug
2669 .as_ref()
2670 .map(|debug_capabiltities| {
2671 translate_debug_capabilities_deprecated(
2672 options,
2673 debug_capabiltities,
2674 all_capability_names,
2675 )
2676 })
2677 .transpose()?,
2678 stop_timeout_ms: env.stop_timeout_ms.map(|s| s.0),
2679 ..Default::default()
2680 })
2681 })
2682 .collect()
2683}
2684
2685fn translate_environments(
2686 options: &CompileOptions<'_>,
2687 envs_in: &Vec<ContextSpanned<ContextEnvironment>>,
2688 all_capability_names: &BTreeSet<Name>,
2689) -> Result<Vec<fdecl::Environment>, Error> {
2690 envs_in
2691 .iter()
2692 .map(|cs_env| {
2693 let env = &cs_env.value;
2694 Ok(fdecl::Environment {
2695 name: Some(env.name.value.clone().into()),
2696 extends: match &env.extends {
2697 Some(spanned) => match spanned.value {
2698 EnvironmentExtends::Realm => Some(fdecl::EnvironmentExtends::Realm),
2699 EnvironmentExtends::None => Some(fdecl::EnvironmentExtends::None),
2700 },
2701 None => Some(fdecl::EnvironmentExtends::None),
2702 },
2703 runners: env
2704 .runners
2705 .as_ref()
2706 .map(|runners| {
2707 runners
2708 .iter()
2709 .map(|r| translate_runner_registration(options, &r.value))
2710 .collect::<Result<Vec<_>, Error>>()
2711 })
2712 .transpose()?,
2713 resolvers: env
2714 .resolvers
2715 .as_ref()
2716 .map(|resolvers| {
2717 resolvers
2718 .iter()
2719 .map(|r| translate_resolver_registration(options, &r.value))
2720 .collect::<Result<Vec<_>, Error>>()
2721 })
2722 .transpose()?,
2723 debug_capabilities: env
2724 .debug
2725 .as_ref()
2726 .map(|debug_capabiltities| {
2727 translate_debug_capabilities(
2728 options,
2729 debug_capabiltities,
2730 all_capability_names,
2731 )
2732 })
2733 .transpose()?,
2734 stop_timeout_ms: env.stop_timeout_ms.clone().map(|s| s.value.0),
2735 ..Default::default()
2736 })
2737 })
2738 .collect()
2739}
2740
2741fn translate_runner_registration_deprecated(
2742 options: &CompileOptions<'_>,
2743 reg: &RunnerRegistration,
2744) -> Result<fdecl::RunnerRegistration, Error> {
2745 let (source, _source_dictionary) = extract_single_offer_source_deprecated(options, reg, None)?;
2746 Ok(fdecl::RunnerRegistration {
2747 source_name: Some(reg.runner.clone().into()),
2748 source: Some(source),
2749 target_name: Some(reg.r#as.as_ref().unwrap_or(®.runner).clone().into()),
2750 ..Default::default()
2751 })
2752}
2753
2754fn translate_resolver_registration_deprecated(
2755 options: &CompileOptions<'_>,
2756 reg: &ResolverRegistration,
2757) -> Result<fdecl::ResolverRegistration, Error> {
2758 let (source, _source_dictionary) = extract_single_offer_source_deprecated(options, reg, None)?;
2759 Ok(fdecl::ResolverRegistration {
2760 resolver: Some(reg.resolver.clone().into()),
2761 source: Some(source),
2762 scheme: Some(
2763 reg.scheme
2764 .as_str()
2765 .parse::<cm_types::UrlScheme>()
2766 .map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))?
2767 .into(),
2768 ),
2769 ..Default::default()
2770 })
2771}
2772
2773fn translate_debug_capabilities_deprecated(
2774 options: &CompileOptions<'_>,
2775 capabilities: &Vec<DebugRegistration>,
2776 all_capability_names: &BTreeSet<&BorrowedName>,
2777) -> Result<Vec<fdecl::DebugRegistration>, Error> {
2778 let mut out_capabilities = vec![];
2779 for capability in capabilities {
2780 if let Some(n) = capability.protocol() {
2781 let (source, _source_dictionary) = extract_single_offer_source_deprecated(
2782 options,
2783 capability,
2784 Some(all_capability_names),
2785 )?;
2786 let targets = all_target_capability_names_deprecated(capability, capability)
2787 .ok_or_else(|| Error::internal("no capability"))?;
2788 let source_names = n;
2789 for target_name in targets {
2790 let source_name = if source_names.len() == 1 {
2799 *source_names.iter().next().unwrap()
2800 } else {
2801 target_name
2802 };
2803 out_capabilities.push(fdecl::DebugRegistration::Protocol(
2804 fdecl::DebugProtocolRegistration {
2805 source: Some(source.clone()),
2806 source_name: Some(source_name.to_string()),
2807 target_name: Some(target_name.to_string()),
2808 ..Default::default()
2809 },
2810 ));
2811 }
2812 }
2813 }
2814 Ok(out_capabilities)
2815}
2816
2817fn translate_runner_registration(
2818 options: &CompileOptions<'_>,
2819 reg: &ContextRunnerRegistration,
2820) -> Result<fdecl::RunnerRegistration, Error> {
2821 let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
2822 Ok(fdecl::RunnerRegistration {
2823 source_name: Some(reg.runner.value.clone().into()),
2824 source: Some(source),
2825 target_name: Some(
2826 reg.r#as.as_ref().map(|s| &s.value).unwrap_or(®.runner.value).to_string(),
2827 ),
2828 ..Default::default()
2829 })
2830}
2831
2832fn translate_resolver_registration(
2833 options: &CompileOptions<'_>,
2834 reg: &ContextResolverRegistration,
2835) -> Result<fdecl::ResolverRegistration, Error> {
2836 let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
2837 Ok(fdecl::ResolverRegistration {
2838 resolver: Some(reg.resolver.value.clone().into()),
2839 source: Some(source),
2840 scheme: Some(
2841 reg.scheme
2842 .value
2843 .as_str()
2844 .parse::<cm_types::UrlScheme>()
2845 .map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))?
2846 .into(),
2847 ),
2848 ..Default::default()
2849 })
2850}
2851
2852fn translate_debug_capabilities(
2853 options: &CompileOptions<'_>,
2854 capabilities: &Vec<ContextSpanned<ContextDebugRegistration>>,
2855 all_capability_names: &BTreeSet<Name>,
2856) -> Result<Vec<fdecl::DebugRegistration>, Error> {
2857 let mut out_capabilities = vec![];
2858 for spanned_capability in capabilities {
2859 let capability = &spanned_capability.value;
2860 if let Some(n) = capability.protocol() {
2861 let (source, _source_dictionary) =
2862 extract_single_offer_source(options, capability, Some(all_capability_names))?;
2863 let targets = all_target_capability_names(capability, capability)
2864 .ok_or_else(|| Error::internal("no capability"))?;
2865 let source_names = n;
2866 for target_name in targets {
2867 let source_name = if source_names.value.len() == 1 {
2876 *source_names.value.iter().next().unwrap()
2877 } else {
2878 target_name
2879 };
2880 out_capabilities.push(fdecl::DebugRegistration::Protocol(
2881 fdecl::DebugProtocolRegistration {
2882 source: Some(source.clone()),
2883 source_name: Some(source_name.to_string()),
2884 target_name: Some(target_name.to_string()),
2885 ..Default::default()
2886 },
2887 ));
2888 }
2889 }
2890 }
2891 Ok(out_capabilities)
2892}
2893
2894fn extract_use_source_deprecated(
2895 options: &CompileOptions<'_>,
2896 in_obj: &Use,
2897 all_capability_names: &BTreeSet<&BorrowedName>,
2898 all_children_names: &BTreeSet<&BorrowedName>,
2899 all_collection_names: Option<&BTreeSet<&BorrowedName>>,
2900) -> Result<(fdecl::Ref, Option<String>), Error> {
2901 let ref_ = match in_obj.from.as_ref() {
2902 Some(UseFromRef::Parent) => fdecl::Ref::Parent(fdecl::ParentRef {}),
2903 Some(UseFromRef::Framework) => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2904 Some(UseFromRef::Debug) => fdecl::Ref::Debug(fdecl::DebugRef {}),
2905 Some(UseFromRef::Self_) => fdecl::Ref::Self_(fdecl::SelfRef {}),
2906 Some(UseFromRef::Named(name)) => {
2907 if all_capability_names.contains(&name.as_ref()) {
2908 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
2909 } else if all_children_names.contains(&name.as_ref()) {
2910 fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
2911 } else if all_collection_names.is_some()
2912 && all_collection_names.unwrap().contains(&name.as_ref())
2913 {
2914 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
2915 } else {
2916 return Err(Error::internal(format!(
2917 "use source \"{:?}\" not supported for \"use from\"",
2918 name
2919 )));
2920 }
2921 }
2922 Some(UseFromRef::Dictionary(d)) => {
2923 return Ok(dictionary_ref_to_source(&d));
2924 }
2925 None => fdecl::Ref::Parent(fdecl::ParentRef {}), };
2927 Ok((ref_, None))
2928}
2929
2930fn extract_use_source(
2931 options: &CompileOptions<'_>,
2932 in_obj: &ContextUse,
2933 all_capability_names: &BTreeSet<Name>,
2934 all_children_names: &BTreeSet<Name>,
2935 all_collection_names: Option<&BTreeSet<Name>>,
2936) -> Result<(fdecl::Ref, Option<String>), Error> {
2937 let ref_ = match in_obj.from.as_ref() {
2938 Some(spanned) => match &spanned.value {
2939 UseFromRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
2940 UseFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
2941 UseFromRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
2942 UseFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
2943 UseFromRef::Named(name) => {
2944 if all_children_names.contains::<BorrowedName>(name.as_ref()) {
2945 fdecl::Ref::Child(fdecl::ChildRef {
2946 name: name.clone().into(),
2947 collection: None,
2948 })
2949 } else if all_collection_names.is_some()
2950 && all_collection_names.unwrap().contains::<BorrowedName>(name.as_ref())
2951 {
2952 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
2953 } else if all_capability_names.contains::<BorrowedName>(name.as_ref()) {
2954 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
2955 } else {
2956 return Err(Error::validate_context(
2957 format!(
2958 "use: from value \"{}\" does not match any child, collection, or capability",
2959 name
2960 ),
2961 Some(spanned.origin.clone()),
2962 ));
2963 }
2964 }
2965 UseFromRef::Dictionary(d) => {
2966 return Ok(dictionary_ref_to_source(&d));
2967 }
2968 },
2969 None => fdecl::Ref::Parent(fdecl::ParentRef {}), };
2971 Ok((ref_, None))
2972}
2973
2974fn extract_use_availability_deprecated(in_obj: &Use) -> Result<fdecl::Availability, Error> {
2975 match in_obj.availability.as_ref() {
2976 Some(Availability::Required) | None => Ok(fdecl::Availability::Required),
2977 Some(Availability::Optional) => Ok(fdecl::Availability::Optional),
2978 Some(Availability::Transitional) => Ok(fdecl::Availability::Transitional),
2979 Some(Availability::SameAsTarget) => Err(Error::internal(
2980 "availability \"same_as_target\" not supported for use declarations",
2981 )),
2982 }
2983}
2984
2985fn extract_use_availability(in_obj: &ContextUse) -> Result<fdecl::Availability, Error> {
2986 match in_obj.availability.as_ref() {
2987 Some(spanned) => match spanned.value {
2988 Availability::Required => Ok(fdecl::Availability::Required),
2989 Availability::Optional => Ok(fdecl::Availability::Optional),
2990 Availability::Transitional => Ok(fdecl::Availability::Transitional),
2991 Availability::SameAsTarget => Err(Error::internal(
2992 "availability \"same_as_target\" not supported for use declarations",
2993 )),
2994 },
2995 None => Ok(fdecl::Availability::Required),
2996 }
2997}
2998
2999fn extract_use_subdir_deprecated(in_obj: &Use) -> Option<cm::RelativePath> {
3000 in_obj.subdir.clone()
3001}
3002
3003fn extract_use_subdir(in_obj: &ContextUse) -> Option<cm::RelativePath> {
3004 in_obj.subdir.clone().map(|s| s.value)
3005}
3006
3007fn extract_expose_subdir_deprecated(in_obj: &Expose) -> Option<cm::RelativePath> {
3008 in_obj.subdir.clone()
3009}
3010
3011fn extract_expose_subdir(in_obj: &ContextExpose) -> Option<cm::RelativePath> {
3012 in_obj.subdir.clone().map(|s| s.value)
3013}
3014
3015fn extract_offer_subdir_deprecated(in_obj: &Offer) -> Option<cm::RelativePath> {
3016 in_obj.subdir.clone()
3017}
3018
3019fn extract_offer_subdir(in_obj: &ContextOffer) -> Option<cm::RelativePath> {
3020 in_obj.subdir.clone().map(|s| s.value)
3021}
3022
3023fn extract_expose_rights_deprecated(in_obj: &Expose) -> Result<Option<fio::Operations>, Error> {
3024 match in_obj.rights.as_ref() {
3025 Some(rights_tokens) => {
3026 let mut rights = Vec::new();
3027 for token in rights_tokens.0.iter() {
3028 rights.append(&mut token.expand())
3029 }
3030 if rights.is_empty() {
3031 return Err(Error::missing_rights(
3032 "Rights provided to expose are not well formed.",
3033 ));
3034 }
3035 let mut seen_rights = BTreeSet::new();
3036 let mut operations: fio::Operations = fio::Operations::empty();
3037 for right in rights.iter() {
3038 if seen_rights.contains(&right) {
3039 return Err(Error::duplicate_rights(
3040 "Rights provided to expose are not well formed.",
3041 ));
3042 }
3043 seen_rights.insert(right);
3044 operations |= *right;
3045 }
3046
3047 Ok(Some(operations))
3048 }
3049 None => Ok(None),
3051 }
3052}
3053
3054fn extract_expose_rights(in_obj: &ContextExpose) -> Result<Option<fio::Operations>, Error> {
3055 match in_obj.rights.as_ref() {
3056 Some(spanned) => {
3057 let rights_tokens = &spanned.value;
3058 let mut rights = Vec::new();
3059 for token in rights_tokens.0.iter() {
3060 rights.append(&mut token.expand())
3061 }
3062 if rights.is_empty() {
3063 return Err(Error::missing_rights(
3064 "Rights provided to expose are not well formed.",
3065 ));
3066 }
3067 let mut seen_rights = BTreeSet::new();
3068 let mut operations: fio::Operations = fio::Operations::empty();
3069 for right in rights.iter() {
3070 if seen_rights.contains(&right) {
3071 return Err(Error::duplicate_rights(
3072 "Rights provided to expose are not well formed.",
3073 ));
3074 }
3075 seen_rights.insert(right);
3076 operations |= *right;
3077 }
3078
3079 Ok(Some(operations))
3080 }
3081 None => Ok(None),
3083 }
3084}
3085
3086fn expose_source_from_ref_deprecated(
3087 options: &CompileOptions<'_>,
3088 reference: &ExposeFromRef,
3089 all_capability_names: Option<&BTreeSet<&BorrowedName>>,
3090 all_collections: Option<&BTreeSet<&BorrowedName>>,
3091) -> (fdecl::Ref, Option<String>) {
3092 let ref_ = match reference {
3093 ExposeFromRef::Named(name) => {
3094 if all_capability_names.is_some()
3095 && all_capability_names.unwrap().contains(&name.as_ref())
3096 {
3097 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
3098 } else if all_collections.is_some() && all_collections.unwrap().contains(&name.as_ref())
3099 {
3100 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
3101 } else {
3102 fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
3103 }
3104 }
3105 ExposeFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3106 ExposeFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
3107 ExposeFromRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
3108 ExposeFromRef::Dictionary(d) => {
3109 return dictionary_ref_to_source(&d);
3110 }
3111 };
3112 (ref_, None)
3113}
3114
3115fn expose_source_from_ref(
3116 options: &CompileOptions<'_>,
3117 reference: &ExposeFromRef,
3118 all_capability_names: Option<&BTreeSet<Name>>,
3119 all_collections: Option<&BTreeSet<Name>>,
3120) -> (fdecl::Ref, Option<String>) {
3121 let ref_ = match reference {
3122 ExposeFromRef::Named(name) => {
3123 if all_capability_names.is_some()
3124 && all_capability_names.unwrap().contains::<BorrowedName>(name.as_ref())
3125 {
3126 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
3127 } else if all_collections.is_some()
3128 && all_collections.unwrap().contains::<BorrowedName>(name.as_ref())
3129 {
3130 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
3131 } else {
3132 fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
3133 }
3134 }
3135 ExposeFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3136 ExposeFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
3137 ExposeFromRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
3138 ExposeFromRef::Dictionary(d) => {
3139 return dictionary_ref_to_source(&d);
3140 }
3141 };
3142 (ref_, None)
3143}
3144
3145fn extract_single_expose_source_deprecated(
3146 options: &CompileOptions<'_>,
3147 in_obj: &Expose,
3148 all_capability_names: Option<&BTreeSet<&BorrowedName>>,
3149) -> Result<(fdecl::Ref, Option<String>), Error> {
3150 match &in_obj.from {
3151 OneOrMany::One(reference) => {
3152 Ok(expose_source_from_ref_deprecated(options, &reference, all_capability_names, None))
3153 }
3154 OneOrMany::Many(many) => Err(Error::internal(format!(
3155 "multiple unexpected \"from\" clauses for \"expose\": {:?}",
3156 many
3157 ))),
3158 }
3159}
3160
3161fn extract_single_expose_source(
3162 options: &CompileOptions<'_>,
3163 in_obj: &ContextExpose,
3164 all_capability_names: Option<&BTreeSet<Name>>,
3165) -> Result<(fdecl::Ref, Option<String>), Error> {
3166 match &in_obj.from.value {
3167 OneOrMany::One(reference) => {
3168 Ok(expose_source_from_ref(options, &reference, all_capability_names, None))
3169 }
3170 OneOrMany::Many(many) => Err(Error::internal(format!(
3171 "multiple unexpected \"from\" clauses for \"expose\": {:?}",
3172 many
3173 ))),
3174 }
3175}
3176
3177fn extract_all_expose_sources_deprecated(
3178 options: &CompileOptions<'_>,
3179 in_obj: &Expose,
3180 all_collections: Option<&BTreeSet<&BorrowedName>>,
3181) -> Vec<(fdecl::Ref, Option<String>)> {
3182 in_obj
3183 .from
3184 .iter()
3185 .map(|e| expose_source_from_ref_deprecated(options, e, None, all_collections))
3186 .collect()
3187}
3188
3189fn extract_all_expose_sources(
3190 options: &CompileOptions<'_>,
3191 in_obj: &ContextExpose,
3192 all_collections: Option<&BTreeSet<Name>>,
3193) -> Vec<(fdecl::Ref, Option<String>)> {
3194 in_obj
3195 .from
3196 .value
3197 .iter()
3198 .map(|e| expose_source_from_ref(options, e, None, all_collections))
3199 .collect()
3200}
3201
3202fn extract_offer_rights_deprecated(in_obj: &Offer) -> Result<Option<fio::Operations>, Error> {
3203 match in_obj.rights.as_ref() {
3204 Some(rights_tokens) => {
3205 let mut rights = Vec::new();
3206 for token in rights_tokens.0.iter() {
3207 rights.append(&mut token.expand())
3208 }
3209 if rights.is_empty() {
3210 return Err(Error::missing_rights("Rights provided to offer are not well formed."));
3211 }
3212 let mut seen_rights = BTreeSet::new();
3213 let mut operations: fio::Operations = fio::Operations::empty();
3214 for right in rights.iter() {
3215 if seen_rights.contains(&right) {
3216 return Err(Error::duplicate_rights(
3217 "Rights provided to offer are not well formed.",
3218 ));
3219 }
3220 seen_rights.insert(right);
3221 operations |= *right;
3222 }
3223
3224 Ok(Some(operations))
3225 }
3226 None => Ok(None),
3228 }
3229}
3230
3231fn extract_offer_rights(in_obj: &ContextOffer) -> Result<Option<fio::Operations>, Error> {
3232 match in_obj.rights.as_ref() {
3233 Some(cs_rights) => {
3234 let rights_token = &cs_rights.value;
3235 let mut rights = Vec::new();
3236 for token in rights_token.0.iter() {
3237 rights.append(&mut token.expand())
3238 }
3239 if rights.is_empty() {
3240 return Err(Error::missing_rights("Rights provided to offer are not well formed."));
3241 }
3242 let mut seen_rights = BTreeSet::new();
3243 let mut operations: fio::Operations = fio::Operations::empty();
3244 for right in rights.iter() {
3245 if seen_rights.contains(&right) {
3246 return Err(Error::duplicate_rights(
3247 "Rights provided to offer are not well formed.",
3248 ));
3249 }
3250 seen_rights.insert(right);
3251 operations |= *right;
3252 }
3253
3254 Ok(Some(operations))
3255 }
3256 None => Ok(None),
3258 }
3259}
3260
3261fn extract_single_offer_source_deprecated<T>(
3262 options: &CompileOptions<'_>,
3263 in_obj: &T,
3264 all_capability_names: Option<&BTreeSet<&BorrowedName>>,
3265) -> Result<(fdecl::Ref, Option<String>), Error>
3266where
3267 T: FromClause,
3268{
3269 match in_obj.from_() {
3270 OneOrMany::One(reference) => {
3271 Ok(any_ref_to_decl_deprecated(options, reference, all_capability_names, None))
3272 }
3273 many => {
3274 return Err(Error::internal(format!(
3275 "multiple unexpected \"from\" clauses for \"offer\": {}",
3276 many
3277 )));
3278 }
3279 }
3280}
3281
3282fn extract_single_offer_source<T>(
3283 options: &CompileOptions<'_>,
3284 in_obj: &T,
3285 all_capability_names: Option<&BTreeSet<Name>>,
3286) -> Result<(fdecl::Ref, Option<String>), Error>
3287where
3288 T: FromClauseContext,
3289{
3290 match in_obj.from_().value {
3291 OneOrMany::One(reference) => {
3292 Ok(any_ref_to_decl(options, reference, all_capability_names, None))
3293 }
3294 many => {
3295 return Err(Error::internal(format!(
3296 "multiple unexpected \"from\" clauses for \"offer\": {}",
3297 many
3298 )));
3299 }
3300 }
3301}
3302
3303fn extract_all_offer_sources_deprecated<T: FromClause>(
3304 options: &CompileOptions<'_>,
3305 in_obj: &T,
3306 all_capability_names: &BTreeSet<&BorrowedName>,
3307 all_collections: &BTreeSet<&BorrowedName>,
3308) -> Vec<(fdecl::Ref, Option<String>)> {
3309 in_obj
3310 .from_()
3311 .into_iter()
3312 .map(|r| {
3313 any_ref_to_decl_deprecated(
3314 options,
3315 r.clone(),
3316 Some(all_capability_names),
3317 Some(all_collections),
3318 )
3319 })
3320 .collect()
3321}
3322
3323fn extract_all_offer_sources<T: FromClauseContext>(
3324 options: &CompileOptions<'_>,
3325 in_obj: &T,
3326 all_capability_names: &BTreeSet<Name>,
3327 all_collections: &BTreeSet<Name>,
3328) -> Vec<(fdecl::Ref, Option<String>)> {
3329 in_obj
3330 .from_()
3331 .value
3332 .into_iter()
3333 .map(|r| {
3334 any_ref_to_decl(options, r.clone(), Some(all_capability_names), Some(all_collections))
3335 })
3336 .collect()
3337}
3338
3339fn translate_target_ref_deprecated(
3340 options: &CompileOptions<'_>,
3341 reference: AnyRef<'_>,
3342 all_children: &BTreeSet<&BorrowedName>,
3343 all_collections: &BTreeSet<&BorrowedName>,
3344 all_capabilities: &BTreeSet<&BorrowedName>,
3345 target_availability: Option<&TargetAvailability>,
3346) -> Result<Option<fdecl::Ref>, Error> {
3347 match reference {
3348 AnyRef::Named(name) if all_children.contains(name) => {
3349 Ok(Some(fdecl::Ref::Child(fdecl::ChildRef {
3350 name: name.to_string(),
3351 collection: None,
3352 })))
3353 }
3354 AnyRef::Named(name) if all_collections.contains(name) => {
3355 Ok(Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })))
3356 }
3357 AnyRef::Named(name) if all_capabilities.contains(name) => {
3358 Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3359 }
3360 AnyRef::OwnDictionary(name) if all_capabilities.contains(name) => {
3361 Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3362 }
3363 AnyRef::Named(_) | AnyRef::OwnDictionary(_)
3364 if target_availability == Some(&TargetAvailability::Unknown) =>
3365 {
3366 Ok(None)
3367 }
3368 AnyRef::Named(_) => Err(Error::internal(format!("dangling reference: \"{}\"", reference))),
3369 _ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))),
3370 }
3371}
3372
3373fn translate_target_ref(
3374 options: &CompileOptions<'_>,
3375 reference: AnyRef<'_>,
3376 all_children: &BTreeSet<Name>,
3377 all_collections: &BTreeSet<Name>,
3378 all_capabilities: &BTreeSet<Name>,
3379 target_availability: Option<&TargetAvailability>,
3380) -> Result<Option<fdecl::Ref>, Error> {
3381 match reference {
3382 AnyRef::Named(name) if all_children.contains::<BorrowedName>(name) => {
3383 Ok(Some(fdecl::Ref::Child(fdecl::ChildRef {
3384 name: name.to_string(),
3385 collection: None,
3386 })))
3387 }
3388 AnyRef::Named(name) if all_collections.contains::<BorrowedName>(name) => {
3389 Ok(Some(fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })))
3390 }
3391 AnyRef::Named(name) if all_capabilities.contains::<BorrowedName>(name) => {
3392 Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3393 }
3394 AnyRef::OwnDictionary(name) if all_capabilities.contains::<BorrowedName>(name) => {
3395 Ok(Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })))
3396 }
3397 AnyRef::Named(_) | AnyRef::OwnDictionary(_)
3398 if target_availability == Some(&TargetAvailability::Unknown) =>
3399 {
3400 Ok(None)
3401 }
3402 AnyRef::Named(_) => Err(Error::internal(format!("dangling reference: \"{}\"", reference))),
3403 _ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))),
3404 }
3405}
3406
3407fn extract_offer_sources_and_targets_deprecated<'a>(
3410 options: &CompileOptions<'_>,
3411 offer: &'a Offer,
3412 source_names: OneOrMany<&'a BorrowedName>,
3413 all_capability_names: &BTreeSet<&'a BorrowedName>,
3414 all_children: &BTreeSet<&'a BorrowedName>,
3415 all_collections: &BTreeSet<&'a BorrowedName>,
3416) -> Result<Vec<(fdecl::Ref, Option<String>, &'a BorrowedName, fdecl::Ref, &'a BorrowedName)>, Error>
3417{
3418 let mut out = vec![];
3419
3420 let sources =
3421 extract_all_offer_sources_deprecated(options, offer, all_capability_names, all_collections);
3422 let target_names = all_target_capability_names_deprecated(offer, offer)
3423 .ok_or_else(|| Error::internal("no capability".to_string()))?;
3424
3425 for (source, source_dictionary) in sources {
3426 for to in &offer.to {
3427 for target_name in &target_names {
3428 let source_name = if source_names.len() == 1 {
3433 source_names.iter().next().unwrap()
3434 } else {
3435 target_name
3436 };
3437 if let Some(target) = translate_target_ref_deprecated(
3438 options,
3439 to.into(),
3440 all_children,
3441 all_collections,
3442 all_capability_names,
3443 offer.target_availability.as_ref(),
3444 )? {
3445 out.push((
3446 source.clone(),
3447 source_dictionary.clone(),
3448 *source_name,
3449 target.clone(),
3450 *target_name,
3451 ));
3452 }
3453 }
3454 }
3455 }
3456 Ok(out)
3457}
3458
3459fn extract_offer_sources_and_targets<'a>(
3462 options: &CompileOptions<'_>,
3463 offer: &'a ContextOffer,
3464 source_names: OneOrMany<&'a BorrowedName>,
3465 all_capability_names: &BTreeSet<Name>,
3466 all_children: &BTreeSet<Name>,
3467 all_collections: &BTreeSet<Name>,
3468) -> Result<Vec<(fdecl::Ref, Option<String>, &'a BorrowedName, fdecl::Ref, &'a BorrowedName)>, Error>
3469{
3470 let mut out = vec![];
3471
3472 let sources = extract_all_offer_sources(options, offer, all_capability_names, all_collections);
3473 let target_names = all_target_capability_names(offer, offer)
3474 .ok_or_else(|| Error::internal("no capability".to_string()))?;
3475
3476 for (source, source_dictionary) in sources {
3477 for to in &offer.to.value {
3478 for target_name in &target_names {
3479 let source_name = if source_names.len() == 1 {
3484 source_names.iter().next().unwrap()
3485 } else {
3486 target_name
3487 };
3488 if let Some(target) = translate_target_ref(
3489 options,
3490 to.into(),
3491 all_children,
3492 all_collections,
3493 all_capability_names,
3494 offer.target_availability.clone().map(|s| s.value).as_ref(),
3495 )? {
3496 out.push((
3497 source.clone(),
3498 source_dictionary.clone(),
3499 *source_name,
3500 target.clone(),
3501 *target_name,
3502 ));
3503 }
3504 }
3505 }
3506 }
3507 Ok(out)
3508}
3509
3510fn all_target_use_paths_deprecated<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<Path>>
3512where
3513 T: CapabilityClause,
3514 U: PathClause,
3515{
3516 if let Some(n) = in_obj.service() {
3517 Some(svc_paths_from_names_deprecated(n, to_obj))
3518 } else if let Some(n) = in_obj.protocol() {
3519 Some(svc_paths_from_names_deprecated(n, to_obj))
3520 } else if let Some(_) = in_obj.directory() {
3521 let path = to_obj.path().expect("no path on use directory");
3522 Some(OneOrMany::One(path.clone()))
3523 } else if let Some(_) = in_obj.storage() {
3524 let path = to_obj.path().expect("no path on use storage");
3525 Some(OneOrMany::One(path.clone()))
3526 } else if let Some(_) = in_obj.event_stream() {
3527 let default_path = Path::new("/svc/fuchsia.component.EventStream").unwrap();
3528 let path = to_obj.path().unwrap_or(&default_path);
3529 Some(OneOrMany::One(path.clone()))
3530 } else {
3531 None
3532 }
3533}
3534
3535fn all_target_use_paths<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<Path>>
3537where
3538 T: ContextCapabilityClause,
3539 U: ContextPathClause,
3540{
3541 if let Some(n) = in_obj.service() {
3542 Some(svc_paths_from_names(n.value, to_obj))
3543 } else if let Some(n) = in_obj.protocol() {
3544 Some(svc_paths_from_names(n.value, to_obj))
3545 } else if let Some(_) = in_obj.directory() {
3546 let path = &to_obj.path().expect("no path on use directory").value;
3547 Some(OneOrMany::One(path.clone()))
3548 } else if let Some(_) = in_obj.storage() {
3549 let path = &to_obj.path().expect("no path on use storage").value;
3550 Some(OneOrMany::One(path.clone()))
3551 } else if let Some(_) = in_obj.event_stream() {
3552 let default_path = Path::new("/svc/fuchsia.component.EventStream").unwrap();
3553 let path = to_obj.path().map(|s| &s.value).unwrap_or(&default_path);
3554 Some(OneOrMany::One(path.clone()))
3555 } else {
3556 None
3557 }
3558}
3559
3560fn svc_paths_from_names_deprecated<T>(
3563 names: OneOrMany<&BorrowedName>,
3564 to_obj: &T,
3565) -> OneOrMany<Path>
3566where
3567 T: PathClause,
3568{
3569 match names {
3570 OneOrMany::One(n) => {
3571 if let Some(path) = to_obj.path() {
3572 OneOrMany::One(path.clone())
3573 } else {
3574 OneOrMany::One(format!("/svc/{}", n).parse().unwrap())
3575 }
3576 }
3577 OneOrMany::Many(v) => {
3578 let many = v.iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect();
3579 OneOrMany::Many(many)
3580 }
3581 }
3582}
3583
3584fn svc_paths_from_names<T>(names: OneOrMany<&BorrowedName>, to_obj: &T) -> OneOrMany<Path>
3587where
3588 T: ContextPathClause,
3589{
3590 match names {
3591 OneOrMany::One(n) => {
3592 if let Some(path) = to_obj.path() {
3593 OneOrMany::One(path.value.clone())
3594 } else {
3595 OneOrMany::One(format!("/svc/{}", n).parse().unwrap())
3596 }
3597 }
3598 OneOrMany::Many(v) => {
3599 let many = v.iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect();
3600 OneOrMany::Many(many)
3601 }
3602 }
3603}
3604
3605fn one_target_use_path_deprecated<T, U>(in_obj: &T, to_obj: &U) -> Result<Path, Error>
3607where
3608 T: CapabilityClause,
3609 U: PathClause,
3610{
3611 match all_target_use_paths_deprecated(in_obj, to_obj) {
3612 Some(OneOrMany::One(target_name)) => Ok(target_name),
3613 Some(OneOrMany::Many(_)) => {
3614 Err(Error::internal("expecting one capability, but multiple provided"))
3615 }
3616 _ => Err(Error::internal("expecting one capability, but none provided")),
3617 }
3618}
3619
3620fn one_target_use_path<T, U>(in_obj: &T, to_obj: &U) -> Result<Path, Error>
3622where
3623 T: ContextCapabilityClause,
3624 U: ContextPathClause,
3625{
3626 match all_target_use_paths(in_obj, to_obj) {
3627 Some(OneOrMany::One(target_name)) => Ok(target_name),
3628 Some(OneOrMany::Many(_)) => {
3629 Err(Error::internal("expecting one capability, but multiple provided"))
3630 }
3631 _ => Err(Error::internal("expecting one capability, but none provided")),
3632 }
3633}
3634
3635fn all_target_capability_names_deprecated<'a, T, U>(
3637 in_obj: &'a T,
3638 to_obj: &'a U,
3639) -> Option<OneOrMany<&'a BorrowedName>>
3640where
3641 T: CapabilityClause,
3642 U: AsClause + PathClause,
3643{
3644 if let Some(as_) = to_obj.r#as() {
3645 Some(OneOrMany::One(as_))
3647 } else {
3648 if let Some(n) = in_obj.service() {
3649 Some(n)
3650 } else if let Some(n) = in_obj.protocol() {
3651 Some(n)
3652 } else if let Some(n) = in_obj.directory() {
3653 Some(n)
3654 } else if let Some(n) = in_obj.storage() {
3655 Some(n)
3656 } else if let Some(n) = in_obj.runner() {
3657 Some(n)
3658 } else if let Some(n) = in_obj.resolver() {
3659 Some(n)
3660 } else if let Some(n) = in_obj.event_stream() {
3661 Some(n)
3662 } else if let Some(n) = in_obj.dictionary() {
3663 Some(n)
3664 } else if let Some(n) = in_obj.config() {
3665 Some(n)
3666 } else {
3667 None
3668 }
3669 }
3670}
3671
3672fn all_target_capability_names<'a, T, U>(
3674 in_obj: &'a T,
3675 to_obj: &'a U,
3676) -> Option<OneOrMany<&'a BorrowedName>>
3677where
3678 T: ContextCapabilityClause,
3679 U: AsClauseContext + ContextPathClause,
3680{
3681 if let Some(as_) = to_obj.r#as() {
3682 Some(OneOrMany::One(as_.value))
3684 } else {
3685 if let Some(n) = in_obj.service() {
3686 Some(n.value)
3687 } else if let Some(n) = in_obj.protocol() {
3688 Some(n.value)
3689 } else if let Some(n) = in_obj.directory() {
3690 Some(n.value)
3691 } else if let Some(n) = in_obj.storage() {
3692 Some(n.value)
3693 } else if let Some(n) = in_obj.runner() {
3694 Some(n.value)
3695 } else if let Some(n) = in_obj.resolver() {
3696 Some(n.value)
3697 } else if let Some(n) = in_obj.event_stream() {
3698 Some(n.value)
3699 } else if let Some(n) = in_obj.dictionary() {
3700 Some(n.value)
3701 } else if let Some(n) = in_obj.config() {
3702 Some(n.value)
3703 } else {
3704 None
3705 }
3706 }
3707}
3708
3709fn extract_expose_target_deprecated(in_obj: &Expose) -> fdecl::Ref {
3710 match &in_obj.to {
3711 Some(ExposeToRef::Parent) => fdecl::Ref::Parent(fdecl::ParentRef {}),
3712 Some(ExposeToRef::Framework) => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3713 None => fdecl::Ref::Parent(fdecl::ParentRef {}),
3714 }
3715}
3716
3717fn extract_expose_target(in_obj: &ContextExpose) -> fdecl::Ref {
3718 match &in_obj.to {
3719 Some(spanned) => match &spanned.value {
3720 ExposeToRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
3721 ExposeToRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
3722 },
3723 None => fdecl::Ref::Parent(fdecl::ParentRef {}),
3724 }
3725}
3726
3727fn extract_environment_ref_deprecated(r: Option<&EnvironmentRef>) -> Option<cm::Name> {
3728 r.map(|r| {
3729 let EnvironmentRef::Named(name) = r;
3730 name.clone()
3731 })
3732}
3733
3734fn extract_environment_ref(r: Option<&ContextSpanned<EnvironmentRef>>) -> Option<cm::Name> {
3735 r.map(|r| {
3736 let EnvironmentRef::Named(name) = &r.value;
3737 name.clone()
3738 })
3739}
3740
3741pub fn translate_capabilities_deprecated(
3742 options: &CompileOptions<'_>,
3743 capabilities_in: &Vec<Capability>,
3744 as_builtin: bool,
3745) -> Result<Vec<fdecl::Capability>, Error> {
3746 let mut out_capabilities = vec![];
3747 for capability in capabilities_in {
3748 if let Some(service) = &capability.service {
3749 for n in service.iter() {
3750 let source_path = match as_builtin {
3751 true => None,
3752 false => Some(
3753 capability
3754 .path
3755 .clone()
3756 .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3757 .into(),
3758 ),
3759 };
3760 out_capabilities.push(fdecl::Capability::Service(fdecl::Service {
3761 name: Some(n.clone().into()),
3762 source_path,
3763 ..Default::default()
3764 }));
3765 }
3766 } else if let Some(protocol) = &capability.protocol {
3767 for n in protocol.iter() {
3768 let source_path = match as_builtin {
3769 true => None,
3770 false => Some(
3771 capability
3772 .path
3773 .clone()
3774 .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3775 .into(),
3776 ),
3777 };
3778 out_capabilities.push(fdecl::Capability::Protocol(fdecl::Protocol {
3779 name: Some(n.clone().into()),
3780 source_path,
3781 #[cfg(fuchsia_api_level_at_least = "HEAD")]
3782 delivery: capability.delivery.map(Into::into),
3783 ..Default::default()
3784 }));
3785 }
3786 } else if let Some(n) = &capability.directory {
3787 let source_path = match as_builtin {
3788 true => None,
3789 false => {
3790 Some(capability.path.as_ref().expect("missing source path").clone().into())
3791 }
3792 };
3793 let rights = extract_required_rights(capability, "capability")?;
3794 out_capabilities.push(fdecl::Capability::Directory(fdecl::Directory {
3795 name: Some(n.clone().into()),
3796 source_path,
3797 rights: Some(rights),
3798 ..Default::default()
3799 }));
3800 } else if let Some(n) = &capability.storage {
3801 if as_builtin {
3802 return Err(Error::internal(format!(
3803 "built-in storage capabilities are not supported"
3804 )));
3805 }
3806 let backing_dir = capability
3807 .backing_dir
3808 .as_ref()
3809 .expect("storage has no path or backing_dir")
3810 .clone()
3811 .into();
3812
3813 let (source, _source_dictionary) = any_ref_to_decl_deprecated(
3814 options,
3815 capability.from.as_ref().unwrap().into(),
3816 None,
3817 None,
3818 );
3819 out_capabilities.push(fdecl::Capability::Storage(fdecl::Storage {
3820 name: Some(n.clone().into()),
3821 backing_dir: Some(backing_dir),
3822 subdir: capability.subdir.clone().map(Into::into),
3823 source: Some(source),
3824 storage_id: Some(
3825 capability.storage_id.clone().expect("storage is missing storage_id").into(),
3826 ),
3827 ..Default::default()
3828 }));
3829 } else if let Some(n) = &capability.runner {
3830 let source_path = match as_builtin {
3831 true => None,
3832 false => {
3833 Some(capability.path.as_ref().expect("missing source path").clone().into())
3834 }
3835 };
3836 out_capabilities.push(fdecl::Capability::Runner(fdecl::Runner {
3837 name: Some(n.clone().into()),
3838 source_path,
3839 ..Default::default()
3840 }));
3841 } else if let Some(n) = &capability.resolver {
3842 let source_path = match as_builtin {
3843 true => None,
3844 false => {
3845 Some(capability.path.as_ref().expect("missing source path").clone().into())
3846 }
3847 };
3848 out_capabilities.push(fdecl::Capability::Resolver(fdecl::Resolver {
3849 name: Some(n.clone().into()),
3850 source_path,
3851 ..Default::default()
3852 }));
3853 } else if let Some(ns) = &capability.event_stream {
3854 if !as_builtin {
3855 return Err(Error::internal(format!(
3856 "event_stream capabilities may only be declared as built-in capabilities"
3857 )));
3858 }
3859 for n in ns {
3860 out_capabilities.push(fdecl::Capability::EventStream(fdecl::EventStream {
3861 name: Some(n.clone().into()),
3862 ..Default::default()
3863 }));
3864 }
3865 } else if let Some(n) = &capability.dictionary {
3866 out_capabilities.push(fdecl::Capability::Dictionary(fdecl::Dictionary {
3867 name: Some(n.clone().into()),
3868 source_path: capability.path.clone().map(Into::into),
3869 ..Default::default()
3870 }));
3871 } else if let Some(c) = &capability.config {
3872 let value = configuration_to_value_deprecated(
3873 c,
3874 &capability,
3875 &capability.config_type,
3876 &capability.value,
3877 )?;
3878 out_capabilities.push(fdecl::Capability::Config(fdecl::Configuration {
3879 name: Some(c.clone().into()),
3880 value: Some(value),
3881 ..Default::default()
3882 }));
3883 } else {
3884 return Err(Error::internal(format!("no capability declaration recognized")));
3885 }
3886 }
3887 Ok(out_capabilities)
3888}
3889
3890pub fn translate_capabilities(
3891 options: &CompileOptions<'_>,
3892 capabilities_in: &Vec<ContextSpanned<ContextCapability>>,
3893 as_builtin: bool,
3894) -> Result<Vec<fdecl::Capability>, Error> {
3895 let mut out_capabilities = vec![];
3896 for cs_capability in capabilities_in {
3897 let capability = &cs_capability.value;
3898 if let Some(service) = &capability.service {
3899 for n in service.value.iter() {
3900 let source_path = match as_builtin {
3901 true => None,
3902 false => Some(
3903 capability
3904 .path
3905 .clone()
3906 .map(|s| s.value)
3907 .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3908 .into(),
3909 ),
3910 };
3911 out_capabilities.push(fdecl::Capability::Service(fdecl::Service {
3912 name: Some(n.clone().into()),
3913 source_path,
3914 ..Default::default()
3915 }));
3916 }
3917 } else if let Some(protocol) = &capability.protocol {
3918 for n in protocol.value.iter() {
3919 let source_path = match as_builtin {
3920 true => None,
3921 false => Some(
3922 capability
3923 .path
3924 .clone()
3925 .map(|s| s.value)
3926 .unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
3927 .into(),
3928 ),
3929 };
3930 out_capabilities.push(fdecl::Capability::Protocol(fdecl::Protocol {
3931 name: Some(n.clone().into()),
3932 source_path,
3933 #[cfg(fuchsia_api_level_at_least = "HEAD")]
3934 delivery: capability.delivery.as_ref().map(|s| s.value.into()),
3935 ..Default::default()
3936 }));
3937 }
3938 } else if let Some(n) = &capability.directory {
3939 let source_path = match as_builtin {
3940 true => None,
3941 false => Some(
3942 capability.path.as_ref().expect("missing source path").value.clone().into(),
3943 ),
3944 };
3945 let rights = extract_required_rights(capability, "capability")?;
3946 out_capabilities.push(fdecl::Capability::Directory(fdecl::Directory {
3947 name: Some(n.value.clone().into()),
3948 source_path,
3949 rights: Some(rights),
3950 ..Default::default()
3951 }));
3952 } else if let Some(n) = &capability.storage {
3953 if as_builtin {
3954 return Err(Error::internal(format!(
3955 "built-in storage capabilities are not supported"
3956 )));
3957 }
3958 let backing_dir = capability
3959 .backing_dir
3960 .as_ref()
3961 .expect("storage has no path or backing_dir")
3962 .value
3963 .clone()
3964 .into();
3965
3966 let (source, _source_dictionary) = any_ref_to_decl(
3967 options,
3968 (&capability.from.as_ref().unwrap().value).into(),
3969 None,
3970 None,
3971 );
3972 out_capabilities.push(fdecl::Capability::Storage(fdecl::Storage {
3973 name: Some(n.value.clone().into()),
3974 backing_dir: Some(backing_dir),
3975 subdir: capability.subdir.as_ref().map(|s| s.value.clone().into()),
3976 source: Some(source),
3977 storage_id: Some(
3978 capability
3979 .storage_id
3980 .as_ref()
3981 .expect("storage is missing storage_id")
3982 .value
3983 .clone()
3984 .into(),
3985 ),
3986 ..Default::default()
3987 }));
3988 } else if let Some(n) = &capability.runner {
3989 let source_path = match as_builtin {
3990 true => None,
3991 false => Some(
3992 capability.path.as_ref().expect("missing source path").value.clone().into(),
3993 ),
3994 };
3995 out_capabilities.push(fdecl::Capability::Runner(fdecl::Runner {
3996 name: Some(n.value.clone().into()),
3997 source_path,
3998 ..Default::default()
3999 }));
4000 } else if let Some(n) = &capability.resolver {
4001 let source_path = match as_builtin {
4002 true => None,
4003 false => Some(
4004 capability.path.as_ref().expect("missing source path").value.clone().into(),
4005 ),
4006 };
4007 out_capabilities.push(fdecl::Capability::Resolver(fdecl::Resolver {
4008 name: Some(n.value.clone().into()),
4009 source_path,
4010 ..Default::default()
4011 }));
4012 } else if let Some(ns) = &capability.event_stream {
4013 if !as_builtin {
4014 return Err(Error::internal(format!(
4015 "event_stream capabilities may only be declared as built-in capabilities"
4016 )));
4017 }
4018 for n in &ns.value {
4019 out_capabilities.push(fdecl::Capability::EventStream(fdecl::EventStream {
4020 name: Some(n.clone().into()),
4021 ..Default::default()
4022 }));
4023 }
4024 } else if let Some(n) = &capability.dictionary {
4025 out_capabilities.push(fdecl::Capability::Dictionary(fdecl::Dictionary {
4026 name: Some(n.value.clone().into()),
4027 source_path: capability.path.as_ref().map(|s| s.value.clone().into()),
4028 ..Default::default()
4029 }));
4030 } else if let Some(c) = &capability.config {
4031 let value = configuration_to_value(
4032 &c.value,
4033 &capability,
4034 &capability.config_type,
4035 &capability.value,
4036 )?;
4037 out_capabilities.push(fdecl::Capability::Config(fdecl::Configuration {
4038 name: Some(c.value.clone().into()),
4039 value: Some(value),
4040 ..Default::default()
4041 }));
4042 } else {
4043 return Err(Error::internal(format!("no capability declaration recognized")));
4044 }
4045 }
4046 Ok(out_capabilities)
4047}
4048
4049pub fn extract_required_rights<T>(in_obj: &T, keyword: &str) -> Result<fio::Operations, Error>
4050where
4051 T: RightsClause,
4052{
4053 match in_obj.rights() {
4054 Some(rights_tokens) => {
4055 let mut rights = Vec::new();
4056 for token in rights_tokens.0.iter() {
4057 rights.append(&mut token.expand())
4058 }
4059 if rights.is_empty() {
4060 return Err(Error::missing_rights(format!(
4061 "Rights provided to `{}` are not well formed.",
4062 keyword
4063 )));
4064 }
4065 let mut seen_rights = BTreeSet::new();
4066 let mut operations: fio::Operations = fio::Operations::empty();
4067 for right in rights.iter() {
4068 if seen_rights.contains(&right) {
4069 return Err(Error::duplicate_rights(format!(
4070 "Rights provided to `{}` are not well formed.",
4071 keyword
4072 )));
4073 }
4074 seen_rights.insert(right);
4075 operations |= *right;
4076 }
4077
4078 Ok(operations)
4079 }
4080 None => Err(Error::internal(format!(
4081 "No `{}` rights provided but required for directories",
4082 keyword
4083 ))),
4084 }
4085}
4086
4087pub fn any_ref_to_decl_deprecated(
4090 options: &CompileOptions<'_>,
4091 reference: AnyRef<'_>,
4092 all_capability_names: Option<&BTreeSet<&BorrowedName>>,
4093 all_collection_names: Option<&BTreeSet<&BorrowedName>>,
4094) -> (fdecl::Ref, Option<String>) {
4095 let ref_ = match reference {
4096 AnyRef::Named(name) => {
4097 if all_capability_names.is_some() && all_capability_names.unwrap().contains(name) {
4098 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4099 } else if all_collection_names.is_some() && all_collection_names.unwrap().contains(name)
4100 {
4101 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
4102 } else {
4103 fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
4104 }
4105 }
4106 AnyRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
4107 AnyRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
4108 AnyRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
4109 AnyRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
4110 AnyRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
4111 AnyRef::Dictionary(d) => {
4112 return dictionary_ref_to_source(&d);
4113 }
4114 AnyRef::OwnDictionary(name) => {
4115 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4116 }
4117 };
4118 (ref_, None)
4119}
4120
4121pub fn any_ref_to_decl(
4124 options: &CompileOptions<'_>,
4125 reference: AnyRef<'_>,
4126 all_capability_names: Option<&BTreeSet<Name>>,
4127 all_collection_names: Option<&BTreeSet<Name>>,
4128) -> (fdecl::Ref, Option<String>) {
4129 let ref_ = match reference {
4130 AnyRef::Named(name) => {
4131 if all_capability_names.is_some()
4132 && all_capability_names.unwrap().contains::<BorrowedName>(name)
4133 {
4134 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4135 } else if all_collection_names.is_some()
4136 && all_collection_names.unwrap().contains::<BorrowedName>(name)
4137 {
4138 fdecl::Ref::Collection(fdecl::CollectionRef { name: name.to_string() })
4139 } else {
4140 fdecl::Ref::Child(fdecl::ChildRef { name: name.to_string(), collection: None })
4141 }
4142 }
4143 AnyRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
4144 AnyRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
4145 AnyRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
4146 AnyRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
4147 AnyRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
4148 AnyRef::Dictionary(d) => {
4149 return dictionary_ref_to_source(&d);
4150 }
4151 AnyRef::OwnDictionary(name) => {
4152 fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.to_string() })
4153 }
4154 };
4155 (ref_, None)
4156}
4157
4158fn dictionary_ref_to_source(d: &DictionaryRef) -> (fdecl::Ref, Option<String>) {
4160 #[allow(unused)]
4161 let root = match &d.root {
4162 RootDictionaryRef::Named(name) => {
4163 fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
4164 }
4165 RootDictionaryRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
4166 RootDictionaryRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
4167 };
4168 (root, Some(d.path.to_string()))
4169}
4170
4171fn configuration_to_value_deprecated(
4172 name: &BorrowedName,
4173 capability: &Capability,
4174 config_type: &Option<ConfigType>,
4175 value: &Option<serde_json::Value>,
4176) -> Result<fdecl::ConfigValue, Error> {
4177 let Some(config_type) = config_type.as_ref() else {
4178 return Err(Error::InvalidArgs(format!(
4179 "Configuration field '{}' must have 'type' set",
4180 name
4181 )));
4182 };
4183 let Some(value) = value.as_ref() else {
4184 return Err(Error::InvalidArgs(format!(
4185 "Configuration field '{}' must have 'value' set",
4186 name
4187 )));
4188 };
4189
4190 let config_type = match config_type {
4191 ConfigType::Bool => cm_rust::ConfigValueType::Bool,
4192 ConfigType::Uint8 => cm_rust::ConfigValueType::Uint8,
4193 ConfigType::Uint16 => cm_rust::ConfigValueType::Uint16,
4194 ConfigType::Uint32 => cm_rust::ConfigValueType::Uint32,
4195 ConfigType::Uint64 => cm_rust::ConfigValueType::Uint64,
4196 ConfigType::Int8 => cm_rust::ConfigValueType::Int8,
4197 ConfigType::Int16 => cm_rust::ConfigValueType::Int16,
4198 ConfigType::Int32 => cm_rust::ConfigValueType::Int32,
4199 ConfigType::Int64 => cm_rust::ConfigValueType::Int64,
4200 ConfigType::String => {
4201 let Some(max_size) = capability.config_max_size else {
4202 return Err(Error::InvalidArgs(format!(
4203 "Configuration field '{}' must have 'max_size' set",
4204 name
4205 )));
4206 };
4207 cm_rust::ConfigValueType::String { max_size: max_size.into() }
4208 }
4209 ConfigType::Vector => {
4210 let Some(ref element) = capability.config_element_type else {
4211 return Err(Error::InvalidArgs(format!(
4212 "Configuration field '{}' must have 'element_type' set",
4213 name
4214 )));
4215 };
4216 let Some(max_count) = capability.config_max_count else {
4217 return Err(Error::InvalidArgs(format!(
4218 "Configuration field '{}' must have 'max_count' set",
4219 name
4220 )));
4221 };
4222 let nested_type = match element {
4223 ConfigNestedValueType::Bool { .. } => cm_rust::ConfigNestedValueType::Bool,
4224 ConfigNestedValueType::Uint8 { .. } => cm_rust::ConfigNestedValueType::Uint8,
4225 ConfigNestedValueType::Uint16 { .. } => cm_rust::ConfigNestedValueType::Uint16,
4226 ConfigNestedValueType::Uint32 { .. } => cm_rust::ConfigNestedValueType::Uint32,
4227 ConfigNestedValueType::Uint64 { .. } => cm_rust::ConfigNestedValueType::Uint64,
4228 ConfigNestedValueType::Int8 { .. } => cm_rust::ConfigNestedValueType::Int8,
4229 ConfigNestedValueType::Int16 { .. } => cm_rust::ConfigNestedValueType::Int16,
4230 ConfigNestedValueType::Int32 { .. } => cm_rust::ConfigNestedValueType::Int32,
4231 ConfigNestedValueType::Int64 { .. } => cm_rust::ConfigNestedValueType::Int64,
4232 ConfigNestedValueType::String { max_size } => {
4233 cm_rust::ConfigNestedValueType::String { max_size: (*max_size).into() }
4234 }
4235 };
4236 cm_rust::ConfigValueType::Vector { max_count: max_count.into(), nested_type }
4237 }
4238 };
4239 let value = config_value_file::field::config_value_from_json_value(value, &config_type)
4240 .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", name, e)))?;
4241 Ok(value.native_into_fidl())
4242}
4243
4244fn configuration_to_value(
4245 name: &BorrowedName,
4246 capability: &ContextCapability,
4247 config_type: &Option<ContextSpanned<ConfigType>>,
4248 value: &Option<ContextSpanned<serde_json::Value>>,
4249) -> Result<fdecl::ConfigValue, Error> {
4250 let Some(config_type) = config_type.as_ref() else {
4251 return Err(Error::InvalidArgs(format!(
4252 "Configuration field '{}' must have 'type' set",
4253 name
4254 )));
4255 };
4256 let Some(value) = value.as_ref() else {
4257 return Err(Error::InvalidArgs(format!(
4258 "Configuration field '{}' must have 'value' set",
4259 name
4260 )));
4261 };
4262
4263 let config_type = match config_type.value {
4264 ConfigType::Bool => cm_rust::ConfigValueType::Bool,
4265 ConfigType::Uint8 => cm_rust::ConfigValueType::Uint8,
4266 ConfigType::Uint16 => cm_rust::ConfigValueType::Uint16,
4267 ConfigType::Uint32 => cm_rust::ConfigValueType::Uint32,
4268 ConfigType::Uint64 => cm_rust::ConfigValueType::Uint64,
4269 ConfigType::Int8 => cm_rust::ConfigValueType::Int8,
4270 ConfigType::Int16 => cm_rust::ConfigValueType::Int16,
4271 ConfigType::Int32 => cm_rust::ConfigValueType::Int32,
4272 ConfigType::Int64 => cm_rust::ConfigValueType::Int64,
4273 ConfigType::String => {
4274 let Some(max_size) = capability.config_max_size.as_ref() else {
4275 return Err(Error::InvalidArgs(format!(
4276 "Configuration field '{}' must have 'max_size' set",
4277 name
4278 )));
4279 };
4280 let size_val: u32 = max_size.value.get();
4281 cm_rust::ConfigValueType::String { max_size: size_val }
4282 }
4283 ConfigType::Vector => {
4284 let Some(ref element) = capability.config_element_type else {
4285 return Err(Error::InvalidArgs(format!(
4286 "Configuration field '{}' must have 'element_type' set",
4287 name
4288 )));
4289 };
4290 let Some(max_count) = capability.config_max_count.as_ref() else {
4291 return Err(Error::InvalidArgs(format!(
4292 "Configuration field '{}' must have 'max_count' set",
4293 name
4294 )));
4295 };
4296 let max_count_val = max_count.value.get();
4297 let nested_type = match element.value {
4298 ConfigNestedValueType::Bool { .. } => cm_rust::ConfigNestedValueType::Bool,
4299 ConfigNestedValueType::Uint8 { .. } => cm_rust::ConfigNestedValueType::Uint8,
4300 ConfigNestedValueType::Uint16 { .. } => cm_rust::ConfigNestedValueType::Uint16,
4301 ConfigNestedValueType::Uint32 { .. } => cm_rust::ConfigNestedValueType::Uint32,
4302 ConfigNestedValueType::Uint64 { .. } => cm_rust::ConfigNestedValueType::Uint64,
4303 ConfigNestedValueType::Int8 { .. } => cm_rust::ConfigNestedValueType::Int8,
4304 ConfigNestedValueType::Int16 { .. } => cm_rust::ConfigNestedValueType::Int16,
4305 ConfigNestedValueType::Int32 { .. } => cm_rust::ConfigNestedValueType::Int32,
4306 ConfigNestedValueType::Int64 { .. } => cm_rust::ConfigNestedValueType::Int64,
4307 ConfigNestedValueType::String { max_size } => {
4308 cm_rust::ConfigNestedValueType::String { max_size: max_size.get().into() }
4309 }
4310 };
4311 cm_rust::ConfigValueType::Vector { max_count: max_count_val.into(), nested_type }
4312 }
4313 };
4314 let value = config_value_file::field::config_value_from_json_value(&value.value, &config_type)
4315 .map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", name, e)))?;
4316 Ok(value.native_into_fidl())
4317}
4318
4319#[cfg(test)]
4320pub mod test_util {
4321 macro_rules! must_parse_cml {
4323 ($($input:tt)+) => {
4324 serde_json::from_str::<Document>(&json!($($input)+).to_string())
4325 .expect("deserialization failed")
4326 };
4327 }
4328 pub(crate) use must_parse_cml;
4329}
4330
4331#[cfg(test)]
4332mod tests {
4333 use super::*;
4334 use crate::error::Error;
4335 use crate::features::Feature;
4336 use crate::translate::test_util::must_parse_cml;
4337 use crate::types::offer::create_offer;
4338 use crate::{
4339 AsClause, CapabilityClause, Document, FromClause, OneOrMany, Path, PathClause, Program,
4340 load_cml_with_context,
4341 };
4342 use assert_matches::assert_matches;
4343 use cm_fidl_validator::error::{AvailabilityList, DeclField, Error as CmFidlError, ErrorList};
4344 use cm_types::{self as cm, Name};
4345 use difference::Changeset;
4346 use serde_json::{Map, Value, json};
4347 use std::collections::BTreeSet;
4348 use std::convert::Into;
4349 use std::str::FromStr;
4350 use {
4351 fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
4352 };
4353
4354 macro_rules! test_compile_context {
4355 (
4356 $(
4357 $(#[$m:meta])*
4358 $test_name:ident => {
4359 $(features = $features:expr,)?
4360 input = $input:expr,
4361 output = $expected:expr,
4362 },
4363 )+
4364 ) => {
4365 $(
4366 $(#[$m])*
4367 #[test]
4368 fn $test_name() {
4369 let fake_file = std::path::Path::new("test.cml");
4370 let input_str = serde_json::to_string(&$input).expect("failed to serialize input json");
4371 let document = crate::load_cml_with_context(&input_str, fake_file).expect("should work");
4372
4373 let options = CompileOptions::new()
4374 .file(&fake_file)
4375 .config_package_path("fake.cvf");
4376
4377 $(
4378 let features = $features;
4379 let options = options.features(&features);
4380 )?
4381
4382 let actual_context = compile_context(&document, options).expect("compilation failed");
4383
4384 if actual_context != $expected {
4385 let e = format!("{:#?}", $expected);
4386 let a = format!("{:#?}", actual_context);
4387 panic!("Test {} failed comparison:\n{}", stringify!($test_name), Changeset::new(&a, &e, "\n"));
4388 }
4389
4390 let input = serde_json::from_str(&$input.to_string()).expect("deserialization failed");
4391 let options = CompileOptions::new().config_package_path("fake.cvf");
4392 $(let features = $features; let options = options.features(&features);)?
4394 let actual_deprecated = compile(&input, options).expect("compilation failed");
4395
4396 if actual_deprecated != $expected {
4397 let e = format!("{:#?}", $expected);
4398 let a = format!("{:#?}", actual_deprecated);
4399 panic!("{}", Changeset::new(&a, &e, "\n"));
4400 }
4401 }
4402 )+
4403 };
4404 }
4405
4406 fn default_component_decl() -> fdecl::Component {
4407 fdecl::Component::default()
4408 }
4409
4410 test_compile_context! {
4411 test_compile_empty_dep => {
4412 input = json!({}),
4413 output = default_component_decl(),
4414 },
4415
4416 test_compile_empty_includes => {
4417 input = json!({ "include": [] }),
4418 output = default_component_decl(),
4419 },
4420
4421 test_compile_offer_to_all_and_diff_sources => {
4422 input = json!({
4423 "children": [
4424 {
4425 "name": "logger",
4426 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4427 },
4428 ],
4429 "collections": [
4430 {
4431 "name": "coll",
4432 "durability": "transient",
4433 },
4434 ],
4435 "offer": [
4436 {
4437 "protocol": "fuchsia.logger.LogSink",
4438 "from": "parent",
4439 "to": "all",
4440 },
4441 {
4442 "protocol": "fuchsia.logger.LogSink",
4443 "from": "framework",
4444 "to": "#logger",
4445 "as": "LogSink2",
4446 },
4447 ],
4448 }),
4449 output = fdecl::Component {
4450 offers: Some(vec![
4451 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4452 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
4453 source_name: Some("fuchsia.logger.LogSink".into()),
4454 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4455 name: "logger".into(),
4456 collection: None,
4457 })),
4458 target_name: Some("LogSink2".into()),
4459 dependency_type: Some(fdecl::DependencyType::Strong),
4460 availability: Some(fdecl::Availability::Required),
4461 ..Default::default()
4462 }),
4463 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4464 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4465 source_name: Some("fuchsia.logger.LogSink".into()),
4466 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4467 name: "logger".into(),
4468 collection: None,
4469 })),
4470 target_name: Some("fuchsia.logger.LogSink".into()),
4471 dependency_type: Some(fdecl::DependencyType::Strong),
4472 availability: Some(fdecl::Availability::Required),
4473 ..Default::default()
4474 }),
4475 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4476 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4477 source_name: Some("fuchsia.logger.LogSink".into()),
4478 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4479 name: "coll".into(),
4480 })),
4481 target_name: Some("fuchsia.logger.LogSink".into()),
4482 dependency_type: Some(fdecl::DependencyType::Strong),
4483 availability: Some(fdecl::Availability::Required),
4484 ..Default::default()
4485 }),
4486 ]),
4487 children: Some(vec![fdecl::Child {
4488 name: Some("logger".into()),
4489 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4490 startup: Some(fdecl::StartupMode::Lazy),
4491 ..Default::default()
4492 }]),
4493 collections: Some(vec![fdecl::Collection {
4494 name: Some("coll".into()),
4495 durability: Some(fdecl::Durability::Transient),
4496 ..Default::default()
4497 }]),
4498 ..default_component_decl()
4499 },
4500 },
4501
4502 test_compile_offer_to_all => {
4503 input = json!({
4504 "children": [
4505 {
4506 "name": "logger",
4507 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4508 },
4509 {
4510 "name": "something",
4511 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4512 },
4513 ],
4514 "collections": [
4515 {
4516 "name": "coll",
4517 "durability": "transient",
4518 },
4519 ],
4520 "offer": [
4521 {
4522 "protocol": "fuchsia.logger.LogSink",
4523 "from": "parent",
4524 "to": "all",
4525 },
4526 {
4527 "protocol": "fuchsia.inspect.InspectSink",
4528 "from": "parent",
4529 "to": "all",
4530 },
4531 {
4532 "protocol": "fuchsia.logger.LegacyLog",
4533 "from": "parent",
4534 "to": "#logger",
4535 },
4536 ],
4537 }),
4538 output = fdecl::Component {
4539 offers: Some(vec![
4540 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4541 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4542 source_name: Some("fuchsia.logger.LegacyLog".into()),
4543 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4544 name: "logger".into(),
4545 collection: None,
4546 })),
4547 target_name: Some("fuchsia.logger.LegacyLog".into()),
4548 dependency_type: Some(fdecl::DependencyType::Strong),
4549 availability: Some(fdecl::Availability::Required),
4550 ..Default::default()
4551 }),
4552 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4553 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4554 source_name: Some("fuchsia.logger.LogSink".into()),
4555 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4556 name: "logger".into(),
4557 collection: None,
4558 })),
4559 target_name: Some("fuchsia.logger.LogSink".into()),
4560 dependency_type: Some(fdecl::DependencyType::Strong),
4561 availability: Some(fdecl::Availability::Required),
4562 ..Default::default()
4563 }),
4564 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4565 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4566 source_name: Some("fuchsia.logger.LogSink".into()),
4567 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4568 name: "something".into(),
4569 collection: None,
4570 })),
4571 target_name: Some("fuchsia.logger.LogSink".into()),
4572 dependency_type: Some(fdecl::DependencyType::Strong),
4573 availability: Some(fdecl::Availability::Required),
4574 ..Default::default()
4575 }),
4576 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4577 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4578 source_name: Some("fuchsia.logger.LogSink".into()),
4579 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4580 name: "coll".into(),
4581 })),
4582 target_name: Some("fuchsia.logger.LogSink".into()),
4583 dependency_type: Some(fdecl::DependencyType::Strong),
4584 availability: Some(fdecl::Availability::Required),
4585 ..Default::default()
4586 }),
4587 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4588 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4589 source_name: Some("fuchsia.inspect.InspectSink".into()),
4590 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4591 name: "logger".into(),
4592 collection: None,
4593 })),
4594 target_name: Some("fuchsia.inspect.InspectSink".into()),
4595 dependency_type: Some(fdecl::DependencyType::Strong),
4596 availability: Some(fdecl::Availability::Required),
4597 ..Default::default()
4598 }),
4599 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4600 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4601 source_name: Some("fuchsia.inspect.InspectSink".into()),
4602 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4603 name: "something".into(),
4604 collection: None,
4605 })),
4606 target_name: Some("fuchsia.inspect.InspectSink".into()),
4607 dependency_type: Some(fdecl::DependencyType::Strong),
4608 availability: Some(fdecl::Availability::Required),
4609 ..Default::default()
4610 }),
4611 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4612 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4613 source_name: Some("fuchsia.inspect.InspectSink".into()),
4614 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4615 name: "coll".into(),
4616 })),
4617 target_name: Some("fuchsia.inspect.InspectSink".into()),
4618 dependency_type: Some(fdecl::DependencyType::Strong),
4619 availability: Some(fdecl::Availability::Required),
4620 ..Default::default()
4621 }),
4622 ]),
4623 children: Some(vec![
4624 fdecl::Child {
4625 name: Some("logger".into()),
4626 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4627 startup: Some(fdecl::StartupMode::Lazy),
4628 ..Default::default()
4629 },
4630 fdecl::Child {
4631 name: Some("something".into()),
4632 url: Some(
4633 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4634 ),
4635 startup: Some(fdecl::StartupMode::Lazy),
4636 ..Default::default()
4637 },
4638 ]),
4639 collections: Some(vec![fdecl::Collection {
4640 name: Some("coll".into()),
4641 durability: Some(fdecl::Durability::Transient),
4642 ..Default::default()
4643 }]),
4644 ..default_component_decl()
4645 },
4646 },
4647
4648 test_compile_offer_to_all_hides_individual_duplicate_routes => {
4649 input = json!({
4650 "children": [
4651 {
4652 "name": "logger",
4653 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4654 },
4655 {
4656 "name": "something",
4657 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4658 },
4659 {
4660 "name": "something-v2",
4661 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
4662 },
4663 ],
4664 "collections": [
4665 {
4666 "name": "coll",
4667 "durability": "transient",
4668 },
4669 {
4670 "name": "coll2",
4671 "durability": "transient",
4672 },
4673 ],
4674 "offer": [
4675 {
4676 "protocol": "fuchsia.logger.LogSink",
4677 "from": "parent",
4678 "to": "#logger",
4679 },
4680 {
4681 "protocol": "fuchsia.logger.LogSink",
4682 "from": "parent",
4683 "to": "all",
4684 },
4685 {
4686 "protocol": "fuchsia.logger.LogSink",
4687 "from": "parent",
4688 "to": [ "#something", "#something-v2", "#coll2"],
4689 },
4690 {
4691 "protocol": "fuchsia.logger.LogSink",
4692 "from": "parent",
4693 "to": "#coll",
4694 },
4695 ],
4696 }),
4697 output = fdecl::Component {
4698 offers: Some(vec![
4699 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4700 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4701 source_name: Some("fuchsia.logger.LogSink".into()),
4702 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4703 name: "logger".into(),
4704 collection: None,
4705 })),
4706 target_name: Some("fuchsia.logger.LogSink".into()),
4707 dependency_type: Some(fdecl::DependencyType::Strong),
4708 availability: Some(fdecl::Availability::Required),
4709 ..Default::default()
4710 }),
4711 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4712 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4713 source_name: Some("fuchsia.logger.LogSink".into()),
4714 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4715 name: "something".into(),
4716 collection: None,
4717 })),
4718 target_name: Some("fuchsia.logger.LogSink".into()),
4719 dependency_type: Some(fdecl::DependencyType::Strong),
4720 availability: Some(fdecl::Availability::Required),
4721 ..Default::default()
4722 }),
4723 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4724 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4725 source_name: Some("fuchsia.logger.LogSink".into()),
4726 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4727 name: "something-v2".into(),
4728 collection: None,
4729 })),
4730 target_name: Some("fuchsia.logger.LogSink".into()),
4731 dependency_type: Some(fdecl::DependencyType::Strong),
4732 availability: Some(fdecl::Availability::Required),
4733 ..Default::default()
4734 }),
4735 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4736 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4737 source_name: Some("fuchsia.logger.LogSink".into()),
4738 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4739 name: "coll2".into(),
4740 })),
4741 target_name: Some("fuchsia.logger.LogSink".into()),
4742 dependency_type: Some(fdecl::DependencyType::Strong),
4743 availability: Some(fdecl::Availability::Required),
4744 ..Default::default()
4745 }),
4746 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4747 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4748 source_name: Some("fuchsia.logger.LogSink".into()),
4749 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
4750 name: "coll".into(),
4751 })),
4752 target_name: Some("fuchsia.logger.LogSink".into()),
4753 dependency_type: Some(fdecl::DependencyType::Strong),
4754 availability: Some(fdecl::Availability::Required),
4755 ..Default::default()
4756 }),
4757 ]),
4758 children: Some(vec![
4759 fdecl::Child {
4760 name: Some("logger".into()),
4761 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4762 startup: Some(fdecl::StartupMode::Lazy),
4763 ..Default::default()
4764 },
4765 fdecl::Child {
4766 name: Some("something".into()),
4767 url: Some(
4768 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4769 ),
4770 startup: Some(fdecl::StartupMode::Lazy),
4771 ..Default::default()
4772 },
4773 fdecl::Child {
4774 name: Some("something-v2".into()),
4775 url: Some(
4776 "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
4777 ),
4778 startup: Some(fdecl::StartupMode::Lazy),
4779 ..Default::default()
4780 },
4781 ]),
4782 collections: Some(vec![fdecl::Collection {
4783 name: Some("coll".into()),
4784 durability: Some(fdecl::Durability::Transient),
4785 ..Default::default()
4786 }, fdecl::Collection {
4787 name: Some("coll2".into()),
4788 durability: Some(fdecl::Durability::Transient),
4789 ..Default::default()
4790 }]),
4791 ..default_component_decl()
4792 },
4793 },
4794
4795 test_compile_offer_to_all_from_child => {
4796 input = json!({
4797 "children": [
4798 {
4799 "name": "logger",
4800 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
4801 },
4802 {
4803 "name": "something",
4804 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4805 },
4806 {
4807 "name": "something-v2",
4808 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
4809 },
4810 ],
4811 "offer": [
4812 {
4813 "protocol": "fuchsia.logger.LogSink",
4814 "from": "#logger",
4815 "to": "all",
4816 },
4817 ],
4818 }),
4819 output = fdecl::Component {
4820 offers: Some(vec![
4821 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4822 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4823 name: "logger".into(),
4824 collection: None,
4825 })),
4826 source_name: Some("fuchsia.logger.LogSink".into()),
4827 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4828 name: "something".into(),
4829 collection: None,
4830 })),
4831 target_name: Some("fuchsia.logger.LogSink".into()),
4832 dependency_type: Some(fdecl::DependencyType::Strong),
4833 availability: Some(fdecl::Availability::Required),
4834 ..Default::default()
4835 }),
4836 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4837 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
4838 name: "logger".into(),
4839 collection: None,
4840 })),
4841 source_name: Some("fuchsia.logger.LogSink".into()),
4842 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4843 name: "something-v2".into(),
4844 collection: None,
4845 })),
4846 target_name: Some("fuchsia.logger.LogSink".into()),
4847 dependency_type: Some(fdecl::DependencyType::Strong),
4848 availability: Some(fdecl::Availability::Required),
4849 ..Default::default()
4850 }),
4851 ]),
4852 children: Some(vec![
4853 fdecl::Child {
4854 name: Some("logger".into()),
4855 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
4856 startup: Some(fdecl::StartupMode::Lazy),
4857 ..Default::default()
4858 },
4859 fdecl::Child {
4860 name: Some("something".into()),
4861 url: Some(
4862 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4863 ),
4864 startup: Some(fdecl::StartupMode::Lazy),
4865 ..Default::default()
4866 },
4867 fdecl::Child {
4868 name: Some("something-v2".into()),
4869 url: Some(
4870 "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
4871 ),
4872 startup: Some(fdecl::StartupMode::Lazy),
4873 ..Default::default()
4874 },
4875 ]),
4876 ..default_component_decl()
4877 },
4878 },
4879
4880 test_compile_offer_multiple_protocols_to_single_array_syntax_and_all => {
4881 input = json!({
4882 "children": [
4883 {
4884 "name": "something",
4885 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4886 },
4887 ],
4888 "offer": [
4889 {
4890 "protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
4891 "from": "parent",
4892 "to": "#something",
4893 },
4894 {
4895 "protocol": "fuchsia.logger.LogSink",
4896 "from": "parent",
4897 "to": "all",
4898 },
4899 ],
4900 }),
4901 output = fdecl::Component {
4902 offers: Some(vec![
4903 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4904 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4905 source_name: Some("fuchsia.logger.LogSink".into()),
4906 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4907 name: "something".into(),
4908 collection: None,
4909 })),
4910 target_name: Some("fuchsia.logger.LogSink".into()),
4911 dependency_type: Some(fdecl::DependencyType::Strong),
4912 availability: Some(fdecl::Availability::Required),
4913 ..Default::default()
4914 }),
4915 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4916 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4917 source_name: Some("fuchsia.inspect.InspectSink".into()),
4918 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4919 name: "something".into(),
4920 collection: None,
4921 })),
4922 target_name: Some("fuchsia.inspect.InspectSink".into()),
4923 dependency_type: Some(fdecl::DependencyType::Strong),
4924 availability: Some(fdecl::Availability::Required),
4925 ..Default::default()
4926 }),
4927 ]),
4928 children: Some(vec![
4929 fdecl::Child {
4930 name: Some("something".into()),
4931 url: Some(
4932 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4933 ),
4934 startup: Some(fdecl::StartupMode::Lazy),
4935 ..Default::default()
4936 },
4937 ]),
4938 ..default_component_decl()
4939 },
4940 },
4941
4942 test_compile_offer_to_all_array_and_single => {
4943 input = json!({
4944 "children": [
4945 {
4946 "name": "something",
4947 "url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
4948 },
4949 ],
4950 "offer": [
4951 {
4952 "protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
4953 "from": "parent",
4954 "to": "all",
4955 },
4956 {
4957 "protocol": "fuchsia.logger.LogSink",
4958 "from": "parent",
4959 "to": "#something",
4960 },
4961 ],
4962 }),
4963 output = fdecl::Component {
4964 offers: Some(vec![
4965 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4966 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4967 source_name: Some("fuchsia.logger.LogSink".into()),
4968 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4969 name: "something".into(),
4970 collection: None,
4971 })),
4972 target_name: Some("fuchsia.logger.LogSink".into()),
4973 dependency_type: Some(fdecl::DependencyType::Strong),
4974 availability: Some(fdecl::Availability::Required),
4975 ..Default::default()
4976 }),
4977 fdecl::Offer::Protocol(fdecl::OfferProtocol {
4978 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
4979 source_name: Some("fuchsia.inspect.InspectSink".into()),
4980 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
4981 name: "something".into(),
4982 collection: None,
4983 })),
4984 target_name: Some("fuchsia.inspect.InspectSink".into()),
4985 dependency_type: Some(fdecl::DependencyType::Strong),
4986 availability: Some(fdecl::Availability::Required),
4987 ..Default::default()
4988 }),
4989 ]),
4990 children: Some(vec![
4991 fdecl::Child {
4992 name: Some("something".into()),
4993 url: Some(
4994 "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
4995 ),
4996 startup: Some(fdecl::StartupMode::Lazy),
4997 ..Default::default()
4998 },
4999 ]),
5000 ..default_component_decl()
5001 },
5002 },
5003
5004 test_compile_program => {
5005 input = json!({
5006 "program": {
5007 "runner": "elf",
5008 "binary": "bin/app",
5009 },
5010 }),
5011 output = fdecl::Component {
5012 program: Some(fdecl::Program {
5013 runner: Some("elf".to_string()),
5014 info: Some(fdata::Dictionary {
5015 entries: Some(vec![fdata::DictionaryEntry {
5016 key: "binary".to_string(),
5017 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5018 }]),
5019 ..Default::default()
5020 }),
5021 ..Default::default()
5022 }),
5023 ..default_component_decl()
5024 },
5025 },
5026
5027 test_compile_program_with_use_runner => {
5028 input = json!({
5029 "program": {
5030 "binary": "bin/app",
5031 },
5032 "use": [
5033 { "runner": "elf", "from": "parent", },
5034 ],
5035 }),
5036 output = fdecl::Component {
5037 program: Some(fdecl::Program {
5038 runner: None,
5039 info: Some(fdata::Dictionary {
5040 entries: Some(vec![fdata::DictionaryEntry {
5041 key: "binary".to_string(),
5042 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5043 }]),
5044 ..Default::default()
5045 }),
5046 ..Default::default()
5047 }),
5048 uses: Some(vec![
5049 fdecl::Use::Runner (
5050 fdecl::UseRunner {
5051 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5052 source_name: Some("elf".to_string()),
5053 ..Default::default()
5054 }
5055 ),
5056 ]),
5057 ..default_component_decl()
5058 },
5059 },
5060
5061 test_compile_program_with_nested_objects => {
5062 input = json!({
5063 "program": {
5064 "runner": "elf",
5065 "binary": "bin/app",
5066 "one": {
5067 "two": {
5068 "three.four": {
5069 "five": "six"
5070 }
5071 },
5072 }
5073 },
5074 }),
5075 output = fdecl::Component {
5076 program: Some(fdecl::Program {
5077 runner: Some("elf".to_string()),
5078 info: Some(fdata::Dictionary {
5079 entries: Some(vec![
5080 fdata::DictionaryEntry {
5081 key: "binary".to_string(),
5082 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5083 },
5084 fdata::DictionaryEntry {
5085 key: "one.two.three.four.five".to_string(),
5086 value: Some(Box::new(fdata::DictionaryValue::Str("six".to_string()))),
5087 },
5088 ]),
5089 ..Default::default()
5090 }),
5091 ..Default::default()
5092 }),
5093 ..default_component_decl()
5094 },
5095 },
5096
5097 test_compile_program_with_array_of_objects => {
5098 input = json!({
5099 "program": {
5100 "runner": "elf",
5101 "binary": "bin/app",
5102 "networks": [
5103 {
5104 "endpoints": [
5105 {
5106 "name": "device",
5107 "mac": "aa:bb:cc:dd:ee:ff"
5108 },
5109 {
5110 "name": "emu",
5111 "mac": "ff:ee:dd:cc:bb:aa"
5112 },
5113 ],
5114 "name": "external_network"
5115 }
5116 ],
5117 },
5118 }),
5119 output = fdecl::Component {
5120 program: Some(fdecl::Program {
5121 runner: Some("elf".to_string()),
5122 info: Some(fdata::Dictionary {
5123 entries: Some(vec![
5124 fdata::DictionaryEntry {
5125 key: "binary".to_string(),
5126 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
5127 },
5128 fdata::DictionaryEntry {
5129 key: "networks".to_string(),
5130 value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
5131 fdata::Dictionary {
5132 entries: Some(vec![
5133 fdata::DictionaryEntry {
5134 key: "endpoints".to_string(),
5135 value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
5136 fdata::Dictionary {
5137 entries: Some(vec![
5138 fdata::DictionaryEntry {
5139 key: "mac".to_string(),
5140 value: Some(Box::new(fdata::DictionaryValue::Str("aa:bb:cc:dd:ee:ff".to_string()))),
5141 },
5142 fdata::DictionaryEntry {
5143 key: "name".to_string(),
5144 value: Some(Box::new(fdata::DictionaryValue::Str("device".to_string()))),
5145 }
5146 ]),
5147 ..Default::default()
5148 },
5149 fdata::Dictionary {
5150 entries: Some(vec![
5151 fdata::DictionaryEntry {
5152 key: "mac".to_string(),
5153 value: Some(Box::new(fdata::DictionaryValue::Str("ff:ee:dd:cc:bb:aa".to_string()))),
5154 },
5155 fdata::DictionaryEntry {
5156 key: "name".to_string(),
5157 value: Some(Box::new(fdata::DictionaryValue::Str("emu".to_string()))),
5158 }
5159 ]),
5160 ..Default::default()
5161 },
5162 ])))
5163 },
5164 fdata::DictionaryEntry {
5165 key: "name".to_string(),
5166 value: Some(Box::new(fdata::DictionaryValue::Str("external_network".to_string()))),
5167 },
5168 ]),
5169 ..Default::default()
5170 }
5171 ]))),
5172 },
5173 ]),
5174 ..Default::default()
5175 }),
5176 ..Default::default()
5177 }),
5178 ..default_component_decl()
5179 },
5180 },
5181
5182 test_compile_use => {
5183 features = FeatureSet::from(vec![Feature::UseDictionaries]),
5184 input = json!({
5185 "use": [
5186 {
5187 "protocol": "LegacyCoolFonts",
5188 "path": "/svc/fuchsia.fonts.LegacyProvider",
5189 "availability": "optional",
5190 },
5191 {
5192 "protocol": "LegacyCoolFonts",
5193 "numbered_handle": 0xab,
5194 },
5195 { "protocol": "fuchsia.sys2.LegacyRealm", "from": "framework" },
5196 { "protocol": "fuchsia.sys2.StorageAdmin", "from": "#data-storage" },
5197 { "protocol": "fuchsia.sys2.DebugProto", "from": "debug" },
5198 { "protocol": "fuchsia.sys2.DictionaryProto", "from": "#logger/in/dict" },
5199 { "protocol": "fuchsia.sys2.Echo", "from": "self", "availability": "transitional" },
5200 { "service": "fuchsia.sys2.EchoService", "from": "parent/dict", },
5201 { "directory": "assets", "rights" : ["read_bytes"], "path": "/data/assets" },
5202 {
5203 "directory": "config",
5204 "path": "/data/config",
5205 "from": "parent",
5206 "rights": ["read_bytes"],
5207 "subdir": "fonts",
5208 },
5209 { "storage": "hippos", "path": "/hippos" },
5210 { "storage": "cache", "path": "/tmp" },
5211 {
5212 "event_stream": "bar_stream",
5213 },
5214 {
5215 "event_stream": ["foobar", "stream"],
5216 "scope": ["#logger", "#modular"],
5217 "path": "/event_stream/another",
5218 },
5219 { "runner": "usain", "from": "parent", },
5220 {
5221 "dictionary": "toolbox",
5222 "path": "/svc",
5223 },
5224 ],
5225 "capabilities": [
5226 { "protocol": "fuchsia.sys2.Echo" },
5227 {
5228 "config": "fuchsia.config.Config",
5229 "type": "bool",
5230 "value": true,
5231 },
5232 {
5233 "storage": "data-storage",
5234 "from": "parent",
5235 "backing_dir": "minfs",
5236 "storage_id": "static_instance_id_or_moniker",
5237 }
5238 ],
5239 "children": [
5240 {
5241 "name": "logger",
5242 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
5243 "environment": "#env_one"
5244 }
5245 ],
5246 "collections": [
5247 {
5248 "name": "modular",
5249 "durability": "transient",
5250 },
5251 ],
5252 "environments": [
5253 {
5254 "name": "env_one",
5255 "extends": "realm",
5256 }
5257 ]
5258 }),
5259 output = fdecl::Component {
5260 uses: Some(vec![
5261 fdecl::Use::Protocol (
5262 fdecl::UseProtocol {
5263 dependency_type: Some(fdecl::DependencyType::Strong),
5264 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5265 source_name: Some("LegacyCoolFonts".to_string()),
5266 target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
5267 availability: Some(fdecl::Availability::Optional),
5268 ..Default::default()
5269 }
5270 ),
5271 fdecl::Use::Protocol (
5272 fdecl::UseProtocol {
5273 dependency_type: Some(fdecl::DependencyType::Strong),
5274 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5275 source_name: Some("LegacyCoolFonts".to_string()),
5276 numbered_handle: Some(0xab),
5277 availability: Some(fdecl::Availability::Required),
5278 ..Default::default()
5279 }
5280 ),
5281 fdecl::Use::Protocol (
5282 fdecl::UseProtocol {
5283 dependency_type: Some(fdecl::DependencyType::Strong),
5284 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
5285 source_name: Some("fuchsia.sys2.LegacyRealm".to_string()),
5286 target_path: Some("/svc/fuchsia.sys2.LegacyRealm".to_string()),
5287 availability: Some(fdecl::Availability::Required),
5288 ..Default::default()
5289 }
5290 ),
5291 fdecl::Use::Protocol (
5292 fdecl::UseProtocol {
5293 dependency_type: Some(fdecl::DependencyType::Strong),
5294 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data-storage".to_string() })),
5295 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
5296 target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
5297 availability: Some(fdecl::Availability::Required),
5298 ..Default::default()
5299 }
5300 ),
5301 fdecl::Use::Protocol (
5302 fdecl::UseProtocol {
5303 dependency_type: Some(fdecl::DependencyType::Strong),
5304 source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
5305 source_name: Some("fuchsia.sys2.DebugProto".to_string()),
5306 target_path: Some("/svc/fuchsia.sys2.DebugProto".to_string()),
5307 availability: Some(fdecl::Availability::Required),
5308 ..Default::default()
5309 }
5310 ),
5311 fdecl::Use::Protocol (
5312 fdecl::UseProtocol {
5313 dependency_type: Some(fdecl::DependencyType::Strong),
5314 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5315 name: "logger".into(),
5316 collection: None,
5317 })),
5318 source_dictionary: Some("in/dict".into()),
5319 source_name: Some("fuchsia.sys2.DictionaryProto".to_string()),
5320 target_path: Some("/svc/fuchsia.sys2.DictionaryProto".to_string()),
5321 availability: Some(fdecl::Availability::Required),
5322 ..Default::default()
5323 }
5324 ),
5325 fdecl::Use::Protocol (
5326 fdecl::UseProtocol {
5327 dependency_type: Some(fdecl::DependencyType::Strong),
5328 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5329 source_name: Some("fuchsia.sys2.Echo".to_string()),
5330 target_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
5331 availability: Some(fdecl::Availability::Transitional),
5332 ..Default::default()
5333 }
5334 ),
5335 fdecl::Use::Service (
5336 fdecl::UseService {
5337 dependency_type: Some(fdecl::DependencyType::Strong),
5338 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5339 source_dictionary: Some("dict".into()),
5340 source_name: Some("fuchsia.sys2.EchoService".to_string()),
5341 target_path: Some("/svc/fuchsia.sys2.EchoService".to_string()),
5342 availability: Some(fdecl::Availability::Required),
5343 ..Default::default()
5344 }
5345 ),
5346 fdecl::Use::Directory (
5347 fdecl::UseDirectory {
5348 dependency_type: Some(fdecl::DependencyType::Strong),
5349 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5350 source_name: Some("assets".to_string()),
5351 target_path: Some("/data/assets".to_string()),
5352 rights: Some(fio::Operations::READ_BYTES),
5353 subdir: None,
5354 availability: Some(fdecl::Availability::Required),
5355 ..Default::default()
5356 }
5357 ),
5358 fdecl::Use::Directory (
5359 fdecl::UseDirectory {
5360 dependency_type: Some(fdecl::DependencyType::Strong),
5361 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5362 source_name: Some("config".to_string()),
5363 target_path: Some("/data/config".to_string()),
5364 rights: Some(fio::Operations::READ_BYTES),
5365 subdir: Some("fonts".to_string()),
5366 availability: Some(fdecl::Availability::Required),
5367 ..Default::default()
5368 }
5369 ),
5370 fdecl::Use::Storage (
5371 fdecl::UseStorage {
5372 source_name: Some("hippos".to_string()),
5373 target_path: Some("/hippos".to_string()),
5374 availability: Some(fdecl::Availability::Required),
5375 ..Default::default()
5376 }
5377 ),
5378 fdecl::Use::Storage (
5379 fdecl::UseStorage {
5380 source_name: Some("cache".to_string()),
5381 target_path: Some("/tmp".to_string()),
5382 availability: Some(fdecl::Availability::Required),
5383 ..Default::default()
5384 }
5385 ),
5386 fdecl::Use::EventStream(fdecl::UseEventStream {
5387 source_name: Some("bar_stream".to_string()),
5388 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5389 target_path: Some("/svc/fuchsia.component.EventStream".to_string()),
5390 availability: Some(fdecl::Availability::Required),
5391 ..Default::default()
5392 }),
5393 fdecl::Use::EventStream(fdecl::UseEventStream {
5394 source_name: Some("foobar".to_string()),
5395 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
5396 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5397 target_path: Some("/event_stream/another".to_string()),
5398 availability: Some(fdecl::Availability::Required),
5399 ..Default::default()
5400 }),
5401 fdecl::Use::EventStream(fdecl::UseEventStream {
5402 source_name: Some("stream".to_string()),
5403 scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
5404 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5405 target_path: Some("/event_stream/another".to_string()),
5406 availability: Some(fdecl::Availability::Required),
5407 ..Default::default()
5408 }),
5409 fdecl::Use::Runner(fdecl::UseRunner {
5410 source_name: Some("usain".to_string()),
5411 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5412 ..Default::default()
5413 }),
5414 fdecl::Use::Dictionary(fdecl::UseDictionary {
5415 source_name: Some("toolbox".to_string()),
5416 source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
5417 target_path: Some("/svc".to_string()),
5418 dependency_type: Some(fdecl::DependencyType::Strong),
5419 availability: Some(fdecl::Availability::Required),
5420 ..Default::default()
5421 }),
5422 ]),
5423 collections: Some(vec![
5424 fdecl::Collection{
5425 name:Some("modular".to_string()),
5426 durability:Some(fdecl::Durability::Transient),
5427 ..Default::default()
5428 },
5429 ]),
5430 capabilities: Some(vec![
5431 fdecl::Capability::Protocol(fdecl::Protocol {
5432 name: Some("fuchsia.sys2.Echo".to_string()),
5433 source_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
5434 ..Default::default()
5435 }),
5436 fdecl::Capability::Config(fdecl::Configuration {
5437 name: Some("fuchsia.config.Config".to_string()),
5438 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
5439 ..Default::default()
5440 }),
5441 fdecl::Capability::Storage(fdecl::Storage {
5442 name: Some("data-storage".to_string()),
5443 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5444 backing_dir: Some("minfs".to_string()),
5445 subdir: None,
5446 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
5447 ..Default::default()
5448 }),
5449 ]),
5450 children: Some(vec![
5451 fdecl::Child{
5452 name:Some("logger".to_string()),
5453 url:Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
5454 startup:Some(fdecl::StartupMode::Lazy),
5455 environment: Some("env_one".to_string()),
5456 ..Default::default()
5457 }
5458 ]),
5459 environments: Some(vec![
5460 fdecl::Environment {
5461 name: Some("env_one".to_string()),
5462 extends: Some(fdecl::EnvironmentExtends::Realm),
5463 ..Default::default()
5464 },
5465 ]),
5466 config: None,
5467 ..default_component_decl()
5468 },
5469 },
5470
5471 test_compile_expose => {
5472 input = json!({
5473 "expose": [
5474 {
5475 "protocol": "fuchsia.logger.Log",
5476 "from": "#logger",
5477 "as": "fuchsia.logger.LegacyLog",
5478 "to": "parent"
5479 },
5480 {
5481 "protocol": [ "A", "B" ],
5482 "from": "self",
5483 "to": "parent"
5484 },
5485 {
5486 "protocol": "C",
5487 "from": "#data-storage",
5488 },
5489 {
5490 "protocol": "D",
5491 "from": "#logger/in/dict",
5492 "as": "E",
5493 },
5494 {
5495 "service": "F",
5496 "from": "#logger/in/dict",
5497 },
5498 {
5499 "service": "svc",
5500 "from": [ "#logger", "#coll", "self" ],
5501 },
5502 {
5503 "directory": "blob",
5504 "from": "self",
5505 "to": "framework",
5506 "rights": ["r*"],
5507 },
5508 {
5509 "directory": [ "blob2", "blob3" ],
5510 "from": "#logger",
5511 "to": "parent",
5512 },
5513 { "directory": "hub", "from": "framework" },
5514 { "runner": "web", "from": "#logger", "to": "parent", "as": "web-rename" },
5515 { "runner": [ "runner_a", "runner_b" ], "from": "#logger" },
5516 { "resolver": "my_resolver", "from": "#logger", "to": "parent", "as": "pkg_resolver" },
5517 { "resolver": [ "resolver_a", "resolver_b" ], "from": "#logger" },
5518 { "dictionary": [ "dictionary_a", "dictionary_b" ], "from": "#logger" },
5519 ],
5520 "capabilities": [
5521 { "protocol": "A" },
5522 { "protocol": "B" },
5523 { "service": "svc" },
5524 {
5525 "directory": "blob",
5526 "path": "/volumes/blobfs/blob",
5527 "rights": ["r*"],
5528 },
5529 {
5530 "runner": "web",
5531 "path": "/svc/fuchsia.component.ComponentRunner",
5532 },
5533 {
5534 "storage": "data-storage",
5535 "from": "parent",
5536 "backing_dir": "minfs",
5537 "storage_id": "static_instance_id_or_moniker",
5538 },
5539 ],
5540 "children": [
5541 {
5542 "name": "logger",
5543 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
5544 },
5545 ],
5546 "collections": [
5547 {
5548 "name": "coll",
5549 "durability": "transient",
5550 },
5551 ],
5552 }),
5553 output = fdecl::Component {
5554 exposes: Some(vec![
5555 fdecl::Expose::Protocol (
5556 fdecl::ExposeProtocol {
5557 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5558 name: "logger".to_string(),
5559 collection: None,
5560 })),
5561 source_name: Some("fuchsia.logger.Log".to_string()),
5562 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5563 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
5564 availability: Some(fdecl::Availability::Required),
5565 ..Default::default()
5566 }
5567 ),
5568 fdecl::Expose::Protocol (
5569 fdecl::ExposeProtocol {
5570 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5571 source_name: Some("A".to_string()),
5572 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5573 target_name: Some("A".to_string()),
5574 availability: Some(fdecl::Availability::Required),
5575 ..Default::default()
5576 }
5577 ),
5578 fdecl::Expose::Protocol (
5579 fdecl::ExposeProtocol {
5580 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5581 source_name: Some("B".to_string()),
5582 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5583 target_name: Some("B".to_string()),
5584 availability: Some(fdecl::Availability::Required),
5585 ..Default::default()
5586 }
5587 ),
5588 fdecl::Expose::Protocol (
5589 fdecl::ExposeProtocol {
5590 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
5591 name: "data-storage".to_string(),
5592 })),
5593 source_name: Some("C".to_string()),
5594 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5595 target_name: Some("C".to_string()),
5596 availability: Some(fdecl::Availability::Required),
5597 ..Default::default()
5598 }
5599 ),
5600 fdecl::Expose::Protocol (
5601 fdecl::ExposeProtocol {
5602 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5603 name: "logger".to_string(),
5604 collection: None,
5605 })),
5606 source_dictionary: Some("in/dict".into()),
5607 source_name: Some("D".to_string()),
5608 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5609 target_name: Some("E".to_string()),
5610 availability: Some(fdecl::Availability::Required),
5611 ..Default::default()
5612 }
5613 ),
5614 fdecl::Expose::Service (
5615 fdecl::ExposeService {
5616 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5617 name: "logger".into(),
5618 collection: None,
5619 })),
5620 source_name: Some("F".into()),
5621 source_dictionary: Some("in/dict".into()),
5622 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5623 target_name: Some("F".into()),
5624 availability: Some(fdecl::Availability::Required),
5625 ..Default::default()
5626 }
5627 ),
5628 fdecl::Expose::Service (
5629 fdecl::ExposeService {
5630 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5631 name: "logger".into(),
5632 collection: None,
5633 })),
5634 source_name: Some("svc".into()),
5635 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5636 target_name: Some("svc".into()),
5637 availability: Some(fdecl::Availability::Required),
5638 ..Default::default()
5639 }
5640 ),
5641 fdecl::Expose::Service (
5642 fdecl::ExposeService {
5643 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
5644 name: "coll".into(),
5645 })),
5646 source_name: Some("svc".into()),
5647 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5648 target_name: Some("svc".into()),
5649 availability: Some(fdecl::Availability::Required),
5650 ..Default::default()
5651 }
5652 ),
5653 fdecl::Expose::Service (
5654 fdecl::ExposeService {
5655 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5656 source_name: Some("svc".into()),
5657 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5658 target_name: Some("svc".into()),
5659 availability: Some(fdecl::Availability::Required),
5660 ..Default::default()
5661 }
5662 ),
5663 fdecl::Expose::Directory (
5664 fdecl::ExposeDirectory {
5665 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
5666 source_name: Some("blob".to_string()),
5667 target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
5668 target_name: Some("blob".to_string()),
5669 rights: Some(
5670 fio::Operations::CONNECT | fio::Operations::ENUMERATE |
5671 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
5672 fio::Operations::GET_ATTRIBUTES
5673 ),
5674 subdir: None,
5675 availability: Some(fdecl::Availability::Required),
5676 ..Default::default()
5677 }
5678 ),
5679 fdecl::Expose::Directory (
5680 fdecl::ExposeDirectory {
5681 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5682 name: "logger".to_string(),
5683 collection: None,
5684 })),
5685 source_name: Some("blob2".to_string()),
5686 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5687 target_name: Some("blob2".to_string()),
5688 rights: None,
5689 subdir: None,
5690 availability: Some(fdecl::Availability::Required),
5691 ..Default::default()
5692 }
5693 ),
5694 fdecl::Expose::Directory (
5695 fdecl::ExposeDirectory {
5696 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5697 name: "logger".to_string(),
5698 collection: None,
5699 })),
5700 source_name: Some("blob3".to_string()),
5701 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5702 target_name: Some("blob3".to_string()),
5703 rights: None,
5704 subdir: None,
5705 availability: Some(fdecl::Availability::Required),
5706 ..Default::default()
5707 }
5708 ),
5709 fdecl::Expose::Directory (
5710 fdecl::ExposeDirectory {
5711 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
5712 source_name: Some("hub".to_string()),
5713 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5714 target_name: Some("hub".to_string()),
5715 rights: None,
5716 subdir: None,
5717 availability: Some(fdecl::Availability::Required),
5718 ..Default::default()
5719 }
5720 ),
5721 fdecl::Expose::Runner (
5722 fdecl::ExposeRunner {
5723 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5724 name: "logger".to_string(),
5725 collection: None,
5726 })),
5727 source_name: Some("web".to_string()),
5728 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5729 target_name: Some("web-rename".to_string()),
5730 ..Default::default()
5731 }
5732 ),
5733 fdecl::Expose::Runner (
5734 fdecl::ExposeRunner {
5735 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5736 name: "logger".to_string(),
5737 collection: None,
5738 })),
5739 source_name: Some("runner_a".to_string()),
5740 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5741 target_name: Some("runner_a".to_string()),
5742 ..Default::default()
5743 }
5744 ),
5745 fdecl::Expose::Runner (
5746 fdecl::ExposeRunner {
5747 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5748 name: "logger".to_string(),
5749 collection: None,
5750 })),
5751 source_name: Some("runner_b".to_string()),
5752 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5753 target_name: Some("runner_b".to_string()),
5754 ..Default::default()
5755 }
5756 ),
5757 fdecl::Expose::Resolver (
5758 fdecl::ExposeResolver {
5759 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5760 name: "logger".to_string(),
5761 collection: None,
5762 })),
5763 source_name: Some("my_resolver".to_string()),
5764 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5765 target_name: Some("pkg_resolver".to_string()),
5766 ..Default::default()
5767 }
5768 ),
5769 fdecl::Expose::Resolver (
5770 fdecl::ExposeResolver {
5771 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5772 name: "logger".to_string(),
5773 collection: None,
5774 })),
5775 source_name: Some("resolver_a".to_string()),
5776 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5777 target_name: Some("resolver_a".to_string()),
5778 ..Default::default()
5779 }
5780 ),
5781 fdecl::Expose::Resolver (
5782 fdecl::ExposeResolver {
5783 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5784 name: "logger".to_string(),
5785 collection: None,
5786 })),
5787 source_name: Some("resolver_b".to_string()),
5788 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5789 target_name: Some("resolver_b".to_string()),
5790 ..Default::default()
5791 }
5792 ),
5793 fdecl::Expose::Dictionary (
5794 fdecl::ExposeDictionary {
5795 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5796 name: "logger".to_string(),
5797 collection: None,
5798 })),
5799 source_name: Some("dictionary_a".to_string()),
5800 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5801 target_name: Some("dictionary_a".to_string()),
5802 availability: Some(fdecl::Availability::Required),
5803 ..Default::default()
5804 }
5805 ),
5806 fdecl::Expose::Dictionary (
5807 fdecl::ExposeDictionary {
5808 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5809 name: "logger".to_string(),
5810 collection: None,
5811 })),
5812 source_name: Some("dictionary_b".to_string()),
5813 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5814 target_name: Some("dictionary_b".to_string()),
5815 availability: Some(fdecl::Availability::Required),
5816 ..Default::default()
5817 }
5818 ),
5819 ]),
5820 offers: None,
5821 capabilities: Some(vec![
5822 fdecl::Capability::Protocol (
5823 fdecl::Protocol {
5824 name: Some("A".to_string()),
5825 source_path: Some("/svc/A".to_string()),
5826 ..Default::default()
5827 }
5828 ),
5829 fdecl::Capability::Protocol (
5830 fdecl::Protocol {
5831 name: Some("B".to_string()),
5832 source_path: Some("/svc/B".to_string()),
5833 ..Default::default()
5834 }
5835 ),
5836 fdecl::Capability::Service (
5837 fdecl::Service {
5838 name: Some("svc".to_string()),
5839 source_path: Some("/svc/svc".to_string()),
5840 ..Default::default()
5841 }
5842 ),
5843 fdecl::Capability::Directory (
5844 fdecl::Directory {
5845 name: Some("blob".to_string()),
5846 source_path: Some("/volumes/blobfs/blob".to_string()),
5847 rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
5848 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
5849 fio::Operations::GET_ATTRIBUTES
5850 ),
5851 ..Default::default()
5852 }
5853 ),
5854 fdecl::Capability::Runner (
5855 fdecl::Runner {
5856 name: Some("web".to_string()),
5857 source_path: Some("/svc/fuchsia.component.ComponentRunner".to_string()),
5858 ..Default::default()
5859 }
5860 ),
5861 fdecl::Capability::Storage(fdecl::Storage {
5862 name: Some("data-storage".to_string()),
5863 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5864 backing_dir: Some("minfs".to_string()),
5865 subdir: None,
5866 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
5867 ..Default::default()
5868 }),
5869 ]),
5870 children: Some(vec![
5871 fdecl::Child {
5872 name: Some("logger".to_string()),
5873 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
5874 startup: Some(fdecl::StartupMode::Lazy),
5875 ..Default::default()
5876 }
5877 ]),
5878 collections: Some(vec![
5879 fdecl::Collection {
5880 name: Some("coll".to_string()),
5881 durability: Some(fdecl::Durability::Transient),
5882 ..Default::default()
5883 }
5884 ]),
5885 ..default_component_decl()
5886 },
5887 },
5888
5889 test_compile_expose_other_availability => {
5890 input = json!({
5891 "expose": [
5892 {
5893 "protocol": "fuchsia.logger.Log",
5894 "from": "#logger",
5895 "as": "fuchsia.logger.LegacyLog_default",
5896 "to": "parent"
5897 },
5898 {
5899 "protocol": "fuchsia.logger.Log",
5900 "from": "#logger",
5901 "as": "fuchsia.logger.LegacyLog_required",
5902 "to": "parent",
5903 "availability": "required"
5904 },
5905 {
5906 "protocol": "fuchsia.logger.Log",
5907 "from": "#logger",
5908 "as": "fuchsia.logger.LegacyLog_optional",
5909 "to": "parent",
5910 "availability": "optional"
5911 },
5912 {
5913 "protocol": "fuchsia.logger.Log",
5914 "from": "#logger",
5915 "as": "fuchsia.logger.LegacyLog_same_as_target",
5916 "to": "parent",
5917 "availability": "same_as_target"
5918 },
5919 {
5920 "protocol": "fuchsia.logger.Log",
5921 "from": "#logger",
5922 "as": "fuchsia.logger.LegacyLog_transitional",
5923 "to": "parent",
5924 "availability": "transitional"
5925 },
5926 ],
5927 "children": [
5928 {
5929 "name": "logger",
5930 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
5931 },
5932 ],
5933 }),
5934 output = fdecl::Component {
5935 exposes: Some(vec![
5936 fdecl::Expose::Protocol (
5937 fdecl::ExposeProtocol {
5938 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5939 name: "logger".to_string(),
5940 collection: None,
5941 })),
5942 source_name: Some("fuchsia.logger.Log".to_string()),
5943 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5944 target_name: Some("fuchsia.logger.LegacyLog_default".to_string()),
5945 availability: Some(fdecl::Availability::Required),
5946 ..Default::default()
5947 }
5948 ),
5949 fdecl::Expose::Protocol (
5950 fdecl::ExposeProtocol {
5951 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5952 name: "logger".to_string(),
5953 collection: None,
5954 })),
5955 source_name: Some("fuchsia.logger.Log".to_string()),
5956 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5957 target_name: Some("fuchsia.logger.LegacyLog_required".to_string()),
5958 availability: Some(fdecl::Availability::Required),
5959 ..Default::default()
5960 }
5961 ),
5962 fdecl::Expose::Protocol (
5963 fdecl::ExposeProtocol {
5964 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5965 name: "logger".to_string(),
5966 collection: None,
5967 })),
5968 source_name: Some("fuchsia.logger.Log".to_string()),
5969 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5970 target_name: Some("fuchsia.logger.LegacyLog_optional".to_string()),
5971 availability: Some(fdecl::Availability::Optional),
5972 ..Default::default()
5973 }
5974 ),
5975 fdecl::Expose::Protocol (
5976 fdecl::ExposeProtocol {
5977 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5978 name: "logger".to_string(),
5979 collection: None,
5980 })),
5981 source_name: Some("fuchsia.logger.Log".to_string()),
5982 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5983 target_name: Some("fuchsia.logger.LegacyLog_same_as_target".to_string()),
5984 availability: Some(fdecl::Availability::SameAsTarget),
5985 ..Default::default()
5986 }
5987 ),
5988 fdecl::Expose::Protocol (
5989 fdecl::ExposeProtocol {
5990 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
5991 name: "logger".to_string(),
5992 collection: None,
5993 })),
5994 source_name: Some("fuchsia.logger.Log".to_string()),
5995 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
5996 target_name: Some("fuchsia.logger.LegacyLog_transitional".to_string()),
5997 availability: Some(fdecl::Availability::Transitional),
5998 ..Default::default()
5999 }
6000 ),
6001 ]),
6002 offers: None,
6003 capabilities: None,
6004 children: Some(vec![
6005 fdecl::Child {
6006 name: Some("logger".to_string()),
6007 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6008 startup: Some(fdecl::StartupMode::Lazy),
6009 environment: None,
6010 on_terminate: None,
6011 ..Default::default()
6012 }
6013 ]),
6014 ..default_component_decl()
6015 },
6016 },
6017
6018 test_compile_expose_source_availability_unknown => {
6019 input = json!({
6020 "expose": [
6021 {
6022 "protocol": "fuchsia.logger.Log",
6023 "from": "#non-existent",
6024 "as": "fuchsia.logger.LegacyLog_non_existent",
6025 "availability": "optional",
6026 "source_availability": "unknown"
6027 },
6028 {
6029 "protocol": "fuchsia.logger.Log",
6030 "from": "#non-existent/dict",
6031 "as": "fuchsia.logger.LegacyLog_non_existent2",
6032 "availability": "optional",
6033 "source_availability": "unknown"
6034 },
6035 {
6036 "protocol": "fuchsia.logger.Log",
6037 "from": "#logger",
6038 "as": "fuchsia.logger.LegacyLog_child_exist",
6039 "availability": "optional",
6040 "source_availability": "unknown"
6041 },
6042 ],
6043 "children": [
6044 {
6045 "name": "logger",
6046 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6047 },
6048 ],
6049 }),
6050 output = fdecl::Component {
6051 exposes: Some(vec![
6052 fdecl::Expose::Protocol (
6053 fdecl::ExposeProtocol {
6054 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6055 source_name: Some("fuchsia.logger.Log".to_string()),
6056 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6057 target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
6058 availability: Some(fdecl::Availability::Optional),
6059 ..Default::default()
6060 }
6061 ),
6062 fdecl::Expose::Protocol (
6063 fdecl::ExposeProtocol {
6064 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6065 source_name: Some("fuchsia.logger.Log".to_string()),
6066 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6067 target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
6068 availability: Some(fdecl::Availability::Optional),
6069 ..Default::default()
6070 }
6071 ),
6072 fdecl::Expose::Protocol (
6073 fdecl::ExposeProtocol {
6074 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6075 name: "logger".to_string(),
6076 collection: None,
6077 })),
6078 source_name: Some("fuchsia.logger.Log".to_string()),
6079 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6080 target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
6081 availability: Some(fdecl::Availability::Optional),
6082 ..Default::default()
6083 }
6084 ),
6085 ]),
6086 children: Some(vec![
6087 fdecl::Child {
6088 name: Some("logger".to_string()),
6089 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6090 startup: Some(fdecl::StartupMode::Lazy),
6091 ..Default::default()
6092 }
6093 ]),
6094 ..default_component_decl()
6095 },
6096 },
6097
6098 test_compile_offer_target_availability_unknown => {
6099 input = json!({
6100 "offer": [
6101 {
6102 "protocol": "fuchsia.logger.Log",
6103 "from": "#logger",
6104 "to": "#non-existent",
6105 "target_availability": "unknown",
6106 },
6107 {
6108 "protocol": "fuchsia.logger.Log",
6109 "from": "#logger",
6110 "to": "self/non-existent-dict",
6111 "target_availability": "unknown",
6112 },
6113 ],
6114 "children": [
6115 {
6116 "name": "logger",
6117 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6118 },
6119 ],
6120 }),
6121 output = fdecl::Component {
6122 offers: Some(vec![]),
6123 children: Some(vec![
6124 fdecl::Child {
6125 name: Some("logger".to_string()),
6126 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6127 startup: Some(fdecl::StartupMode::Lazy),
6128 ..Default::default()
6129 },
6130 ]),
6131 ..default_component_decl()
6132 },
6133 },
6134
6135 test_compile_offer_source_availability_unknown => {
6136 input = json!({
6137 "offer": [
6138 {
6139 "protocol": "fuchsia.logger.Log",
6140 "from": "#non-existent",
6141 "as": "fuchsia.logger.LegacyLog_non_existent",
6142 "to": "#target",
6143 "availability": "optional",
6144 "source_availability": "unknown"
6145 },
6146 {
6147 "protocol": "fuchsia.logger.Log",
6148 "from": "#non-existent/dict",
6149 "as": "fuchsia.logger.LegacyLog_non_existent2",
6150 "to": "#target",
6151 "availability": "optional",
6152 "source_availability": "unknown"
6153 },
6154 {
6155 "protocol": "fuchsia.logger.Log",
6156 "from": "#logger",
6157 "as": "fuchsia.logger.LegacyLog_child_exist",
6158 "to": "#target",
6159 "availability": "optional",
6160 "source_availability": "unknown"
6161 },
6162 ],
6163 "children": [
6164 {
6165 "name": "logger",
6166 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6167 },
6168 {
6169 "name": "target",
6170 "url": "#meta/target.cm"
6171 },
6172 ],
6173 }),
6174 output = fdecl::Component {
6175 offers: Some(vec![
6176 fdecl::Offer::Protocol (
6177 fdecl::OfferProtocol {
6178 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6179 source_name: Some("fuchsia.logger.Log".to_string()),
6180 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6181 name: "target".to_string(),
6182 collection: None,
6183 })),
6184 target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
6185 dependency_type: Some(fdecl::DependencyType::Strong),
6186 availability: Some(fdecl::Availability::Optional),
6187 ..Default::default()
6188 }
6189 ),
6190 fdecl::Offer::Protocol (
6191 fdecl::OfferProtocol {
6192 source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
6193 source_name: Some("fuchsia.logger.Log".to_string()),
6194 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6195 name: "target".to_string(),
6196 collection: None,
6197 })),
6198 target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
6199 dependency_type: Some(fdecl::DependencyType::Strong),
6200 availability: Some(fdecl::Availability::Optional),
6201 ..Default::default()
6202 }
6203 ),
6204 fdecl::Offer::Protocol (
6205 fdecl::OfferProtocol {
6206 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6207 name: "logger".to_string(),
6208 collection: None,
6209 })),
6210 source_name: Some("fuchsia.logger.Log".to_string()),
6211 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6212 name: "target".to_string(),
6213 collection: None,
6214 })),
6215 target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
6216 dependency_type: Some(fdecl::DependencyType::Strong),
6217 availability: Some(fdecl::Availability::Optional),
6218 ..Default::default()
6219 }
6220 ),
6221 ]),
6222 children: Some(vec![
6223 fdecl::Child {
6224 name: Some("logger".to_string()),
6225 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6226 startup: Some(fdecl::StartupMode::Lazy),
6227 ..Default::default()
6228 },
6229 fdecl::Child {
6230 name: Some("target".to_string()),
6231 url: Some("#meta/target.cm".to_string()),
6232 startup: Some(fdecl::StartupMode::Lazy),
6233 ..Default::default()
6234 },
6235 ]),
6236 ..default_component_decl()
6237 },
6238 },
6239
6240 test_compile_offer => {
6241 input = json!({
6242 "offer": [
6243 {
6244 "protocol": "fuchsia.logger.LegacyLog",
6245 "from": "#logger",
6246 "to": "#netstack", "dependency": "weak"
6248 },
6249 {
6250 "protocol": "fuchsia.logger.LegacyLog",
6251 "from": "#logger",
6252 "to": [ "#modular" ], "as": "fuchsia.logger.LegacySysLog",
6254 "dependency": "strong"
6255 },
6256 {
6257 "protocol": [
6258 "fuchsia.setui.SetUiService",
6259 "fuchsia.test.service.Name"
6260 ],
6261 "from": "parent",
6262 "to": [ "#modular" ],
6263 "availability": "optional"
6264 },
6265 {
6266 "protocol": "fuchsia.sys2.StorageAdmin",
6267 "from": "#data",
6268 "to": [ "#modular" ],
6269 },
6270 {
6271 "protocol": "fuchsia.sys2.FromDict",
6272 "from": "parent/in/dict",
6273 "to": [ "#modular" ],
6274 },
6275 {
6276 "service": "svc",
6277 "from": [ "parent", "self", "#logger", "#modular" ],
6278 "to": "#netstack",
6279 },
6280 {
6281 "service": "fuchsia.sys2.FromDictService",
6282 "from": [ "parent/in/dict"],
6283 "to": "#modular",
6284 "dependency": "weak",
6285 },
6286 {
6287 "directory": "assets",
6288 "from": "parent",
6289 "to": [ "#netstack" ],
6290 "dependency": "weak",
6291 "availability": "same_as_target"
6292 },
6293 {
6294 "directory": [ "assets2", "assets3" ],
6295 "from": "parent",
6296 "to": [ "#modular", "#netstack" ],
6297 },
6298 {
6299 "directory": "data",
6300 "from": "parent",
6301 "to": [ "#modular" ],
6302 "as": "assets",
6303 "subdir": "index/file",
6304 "dependency": "strong"
6305 },
6306 {
6307 "directory": "hub",
6308 "from": "framework",
6309 "to": [ "#modular" ],
6310 "as": "hub",
6311 },
6312 {
6313 "storage": "data",
6314 "from": "self",
6315 "to": [
6316 "#netstack",
6317 "#modular"
6318 ],
6319 },
6320 {
6321 "storage": [ "storage_a", "storage_b" ],
6322 "from": "parent",
6323 "to": "#netstack",
6324 },
6325 {
6326 "runner": "elf",
6327 "from": "parent",
6328 "to": [ "#modular" ],
6329 "as": "elf-renamed",
6330 },
6331 {
6332 "runner": [ "runner_a", "runner_b" ],
6333 "from": "parent",
6334 "to": "#netstack",
6335 },
6336 {
6337 "resolver": "my_resolver",
6338 "from": "parent",
6339 "to": [ "#modular" ],
6340 "as": "pkg_resolver",
6341 },
6342 {
6343 "resolver": [ "resolver_a", "resolver_b" ],
6344 "from": "parent",
6345 "to": "#netstack",
6346 },
6347 {
6348 "dictionary": [ "dictionary_a", "dictionary_b" ],
6349 "from": "parent",
6350 "to": "#netstack",
6351 },
6352 {
6353 "event_stream": [
6354 "running",
6355 "started",
6356 ],
6357 "from": "parent",
6358 "to": "#netstack",
6359 },
6360 {
6361 "event_stream": "stopped",
6362 "from": "parent",
6363 "to": "#netstack",
6364 "as": "some_other_event",
6365 },
6366 ],
6367 "children": [
6368 {
6369 "name": "logger",
6370 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
6371 },
6372 {
6373 "name": "netstack",
6374 "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm"
6375 },
6376 ],
6377 "collections": [
6378 {
6379 "name": "modular",
6380 "durability": "transient",
6381 },
6382 ],
6383 "capabilities": [
6384 {
6385 "service": "svc",
6386 },
6387 {
6388 "storage": "data",
6389 "backing_dir": "minfs",
6390 "from": "#logger",
6391 "storage_id": "static_instance_id_or_moniker",
6392 },
6393 ],
6394 }),
6395 output = fdecl::Component {
6396 offers: Some(vec![
6397 fdecl::Offer::Protocol (
6398 fdecl::OfferProtocol {
6399 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6400 name: "logger".to_string(),
6401 collection: None,
6402 })),
6403 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6404 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6405 name: "netstack".to_string(),
6406 collection: None,
6407 })),
6408 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
6409 dependency_type: Some(fdecl::DependencyType::Weak),
6410 availability: Some(fdecl::Availability::Required),
6411 ..Default::default()
6412 }
6413 ),
6414 fdecl::Offer::Protocol (
6415 fdecl::OfferProtocol {
6416 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6417 name: "logger".to_string(),
6418 collection: None,
6419 })),
6420 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
6421 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6422 name: "modular".to_string(),
6423 })),
6424 target_name: Some("fuchsia.logger.LegacySysLog".to_string()),
6425 dependency_type: Some(fdecl::DependencyType::Strong),
6426 availability: Some(fdecl::Availability::Required),
6427 ..Default::default()
6428 }
6429 ),
6430 fdecl::Offer::Protocol (
6431 fdecl::OfferProtocol {
6432 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6433 source_name: Some("fuchsia.setui.SetUiService".to_string()),
6434 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6435 name: "modular".to_string(),
6436 })),
6437 target_name: Some("fuchsia.setui.SetUiService".to_string()),
6438 dependency_type: Some(fdecl::DependencyType::Strong),
6439 availability: Some(fdecl::Availability::Optional),
6440 ..Default::default()
6441 }
6442 ),
6443 fdecl::Offer::Protocol (
6444 fdecl::OfferProtocol {
6445 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6446 source_name: Some("fuchsia.test.service.Name".to_string()),
6447 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6448 name: "modular".to_string(),
6449 })),
6450 target_name: Some("fuchsia.test.service.Name".to_string()),
6451 dependency_type: Some(fdecl::DependencyType::Strong),
6452 availability: Some(fdecl::Availability::Optional),
6453 ..Default::default()
6454 }
6455 ),
6456 fdecl::Offer::Protocol (
6457 fdecl::OfferProtocol {
6458 source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6459 name: "data".to_string(),
6460 })),
6461 source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6462 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6463 name: "modular".to_string(),
6464 })),
6465 target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
6466 dependency_type: Some(fdecl::DependencyType::Strong),
6467 availability: Some(fdecl::Availability::Required),
6468 ..Default::default()
6469 }
6470 ),
6471 fdecl::Offer::Protocol (
6472 fdecl::OfferProtocol {
6473 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6474 source_dictionary: Some("in/dict".into()),
6475 source_name: Some("fuchsia.sys2.FromDict".to_string()),
6476 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6477 name: "modular".to_string(),
6478 })),
6479 target_name: Some("fuchsia.sys2.FromDict".to_string()),
6480 dependency_type: Some(fdecl::DependencyType::Strong),
6481 availability: Some(fdecl::Availability::Required),
6482 ..Default::default()
6483 }
6484 ),
6485 fdecl::Offer::Service (
6486 fdecl::OfferService {
6487 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6488 source_name: Some("svc".into()),
6489 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6490 name: "netstack".into(),
6491 collection: None,
6492 })),
6493 target_name: Some("svc".into()),
6494 availability: Some(fdecl::Availability::Required),
6495 dependency_type: Some(fdecl::DependencyType::Strong),
6496 ..Default::default()
6497 }
6498 ),
6499 fdecl::Offer::Service (
6500 fdecl::OfferService {
6501 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
6502 source_name: Some("svc".into()),
6503 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6504 name: "netstack".into(),
6505 collection: None,
6506 })),
6507 target_name: Some("svc".into()),
6508 availability: Some(fdecl::Availability::Required),
6509 dependency_type: Some(fdecl::DependencyType::Strong),
6510 ..Default::default()
6511 }
6512 ),
6513 fdecl::Offer::Service (
6514 fdecl::OfferService {
6515 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6516 name: "logger".into(),
6517 collection: None,
6518 })),
6519 source_name: Some("svc".into()),
6520 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6521 name: "netstack".into(),
6522 collection: None,
6523 })),
6524 target_name: Some("svc".into()),
6525 availability: Some(fdecl::Availability::Required),
6526 dependency_type: Some(fdecl::DependencyType::Strong),
6527 ..Default::default()
6528 }
6529 ),
6530 fdecl::Offer::Service (
6531 fdecl::OfferService {
6532 source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6533 name: "modular".into(),
6534 })),
6535 source_name: Some("svc".into()),
6536 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6537 name: "netstack".into(),
6538 collection: None,
6539 })),
6540 target_name: Some("svc".into()),
6541 availability: Some(fdecl::Availability::Required),
6542 dependency_type: Some(fdecl::DependencyType::Strong),
6543 ..Default::default()
6544 }
6545 ),
6546 fdecl::Offer::Service (
6547 fdecl::OfferService {
6548 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6549 source_name: Some("fuchsia.sys2.FromDictService".into()),
6550 source_dictionary: Some("in/dict".into()),
6551 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6552 name: "modular".into(),
6553 })),
6554 target_name: Some("fuchsia.sys2.FromDictService".to_string()),
6555 availability: Some(fdecl::Availability::Required),
6556 dependency_type: Some(fdecl::DependencyType::Weak),
6557 ..Default::default()
6558 }
6559 ),
6560 fdecl::Offer::Directory (
6561 fdecl::OfferDirectory {
6562 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6563 source_name: Some("assets".to_string()),
6564 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6565 name: "netstack".to_string(),
6566 collection: None,
6567 })),
6568 target_name: Some("assets".to_string()),
6569 rights: None,
6570 subdir: None,
6571 dependency_type: Some(fdecl::DependencyType::Weak),
6572 availability: Some(fdecl::Availability::SameAsTarget),
6573 ..Default::default()
6574 }
6575 ),
6576 fdecl::Offer::Directory (
6577 fdecl::OfferDirectory {
6578 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6579 source_name: Some("assets2".to_string()),
6580 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6581 name: "modular".to_string(),
6582 })),
6583 target_name: Some("assets2".to_string()),
6584 rights: None,
6585 subdir: None,
6586 dependency_type: Some(fdecl::DependencyType::Strong),
6587 availability: Some(fdecl::Availability::Required),
6588 ..Default::default()
6589 }
6590 ),
6591 fdecl::Offer::Directory (
6592 fdecl::OfferDirectory {
6593 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6594 source_name: Some("assets3".to_string()),
6595 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6596 name: "modular".to_string(),
6597 })),
6598 target_name: Some("assets3".to_string()),
6599 rights: None,
6600 subdir: None,
6601 dependency_type: Some(fdecl::DependencyType::Strong),
6602 availability: Some(fdecl::Availability::Required),
6603 ..Default::default()
6604 }
6605 ),
6606 fdecl::Offer::Directory (
6607 fdecl::OfferDirectory {
6608 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6609 source_name: Some("assets2".to_string()),
6610 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6611 name: "netstack".to_string(),
6612 collection: None,
6613 })),
6614 target_name: Some("assets2".to_string()),
6615 rights: None,
6616 subdir: None,
6617 dependency_type: Some(fdecl::DependencyType::Strong),
6618 availability: Some(fdecl::Availability::Required),
6619 ..Default::default()
6620 }
6621 ),
6622 fdecl::Offer::Directory (
6623 fdecl::OfferDirectory {
6624 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6625 source_name: Some("assets3".to_string()),
6626 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6627 name: "netstack".to_string(),
6628 collection: None,
6629 })),
6630 target_name: Some("assets3".to_string()),
6631 rights: None,
6632 subdir: None,
6633 dependency_type: Some(fdecl::DependencyType::Strong),
6634 availability: Some(fdecl::Availability::Required),
6635 ..Default::default()
6636 }
6637 ),
6638 fdecl::Offer::Directory (
6639 fdecl::OfferDirectory {
6640 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6641 source_name: Some("data".to_string()),
6642 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6643 name: "modular".to_string(),
6644 })),
6645 target_name: Some("assets".to_string()),
6646 rights: None,
6647 subdir: Some("index/file".to_string()),
6648 dependency_type: Some(fdecl::DependencyType::Strong),
6649 availability: Some(fdecl::Availability::Required),
6650 ..Default::default()
6651 }
6652 ),
6653 fdecl::Offer::Directory (
6654 fdecl::OfferDirectory {
6655 source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
6656 source_name: Some("hub".to_string()),
6657 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6658 name: "modular".to_string(),
6659 })),
6660 target_name: Some("hub".to_string()),
6661 rights: None,
6662 subdir: None,
6663 dependency_type: Some(fdecl::DependencyType::Strong),
6664 availability: Some(fdecl::Availability::Required),
6665 ..Default::default()
6666 }
6667 ),
6668 fdecl::Offer::Storage (
6669 fdecl::OfferStorage {
6670 source_name: Some("data".to_string()),
6671 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
6672 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6673 name: "netstack".to_string(),
6674 collection: None,
6675 })),
6676 target_name: Some("data".to_string()),
6677 availability: Some(fdecl::Availability::Required),
6678 ..Default::default()
6679 }
6680 ),
6681 fdecl::Offer::Storage (
6682 fdecl::OfferStorage {
6683 source_name: Some("data".to_string()),
6684 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
6685 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6686 name: "modular".to_string(),
6687 })),
6688 target_name: Some("data".to_string()),
6689 availability: Some(fdecl::Availability::Required),
6690 ..Default::default()
6691 }
6692 ),
6693 fdecl::Offer::Storage (
6694 fdecl::OfferStorage {
6695 source_name: Some("storage_a".to_string()),
6696 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6697 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6698 name: "netstack".to_string(),
6699 collection: None,
6700 })),
6701 target_name: Some("storage_a".to_string()),
6702 availability: Some(fdecl::Availability::Required),
6703 ..Default::default()
6704 }
6705 ),
6706 fdecl::Offer::Storage (
6707 fdecl::OfferStorage {
6708 source_name: Some("storage_b".to_string()),
6709 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6710 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6711 name: "netstack".to_string(),
6712 collection: None,
6713 })),
6714 target_name: Some("storage_b".to_string()),
6715 availability: Some(fdecl::Availability::Required),
6716 ..Default::default()
6717 }
6718 ),
6719 fdecl::Offer::Runner (
6720 fdecl::OfferRunner {
6721 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6722 source_name: Some("elf".to_string()),
6723 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6724 name: "modular".to_string(),
6725 })),
6726 target_name: Some("elf-renamed".to_string()),
6727 ..Default::default()
6728 }
6729 ),
6730 fdecl::Offer::Runner (
6731 fdecl::OfferRunner {
6732 source_name: Some("runner_a".to_string()),
6733 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6734 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6735 name: "netstack".to_string(),
6736 collection: None,
6737 })),
6738 target_name: Some("runner_a".to_string()),
6739 ..Default::default()
6740 }
6741 ),
6742 fdecl::Offer::Runner (
6743 fdecl::OfferRunner {
6744 source_name: Some("runner_b".to_string()),
6745 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6746 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6747 name: "netstack".to_string(),
6748 collection: None,
6749 })),
6750 target_name: Some("runner_b".to_string()),
6751 ..Default::default()
6752 }
6753 ),
6754 fdecl::Offer::Resolver (
6755 fdecl::OfferResolver {
6756 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6757 source_name: Some("my_resolver".to_string()),
6758 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
6759 name: "modular".to_string(),
6760 })),
6761 target_name: Some("pkg_resolver".to_string()),
6762 ..Default::default()
6763 }
6764 ),
6765 fdecl::Offer::Resolver (
6766 fdecl::OfferResolver {
6767 source_name: Some("resolver_a".to_string()),
6768 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6769 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6770 name: "netstack".to_string(),
6771 collection: None,
6772 })),
6773 target_name: Some("resolver_a".to_string()),
6774 ..Default::default()
6775 }
6776 ),
6777 fdecl::Offer::Resolver (
6778 fdecl::OfferResolver {
6779 source_name: Some("resolver_b".to_string()),
6780 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6781 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6782 name: "netstack".to_string(),
6783 collection: None,
6784 })),
6785 target_name: Some("resolver_b".to_string()),
6786 ..Default::default()
6787 }
6788 ),
6789 fdecl::Offer::Dictionary (
6790 fdecl::OfferDictionary {
6791 source_name: Some("dictionary_a".to_string()),
6792 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6793 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6794 name: "netstack".to_string(),
6795 collection: None,
6796 })),
6797 target_name: Some("dictionary_a".to_string()),
6798 dependency_type: Some(fdecl::DependencyType::Strong),
6799 availability: Some(fdecl::Availability::Required),
6800 ..Default::default()
6801 }
6802 ),
6803 fdecl::Offer::Dictionary (
6804 fdecl::OfferDictionary {
6805 source_name: Some("dictionary_b".to_string()),
6806 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6807 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6808 name: "netstack".to_string(),
6809 collection: None,
6810 })),
6811 target_name: Some("dictionary_b".to_string()),
6812 dependency_type: Some(fdecl::DependencyType::Strong),
6813 availability: Some(fdecl::Availability::Required),
6814 ..Default::default()
6815 }
6816 ),
6817 fdecl::Offer::EventStream (
6818 fdecl::OfferEventStream {
6819 source_name: Some("running".to_string()),
6820 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6821 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6822 name: "netstack".to_string(),
6823 collection: None,
6824 })),
6825 target_name: Some("running".to_string()),
6826 availability: Some(fdecl::Availability::Required),
6827 ..Default::default()
6828 }
6829 ),
6830 fdecl::Offer::EventStream (
6831 fdecl::OfferEventStream {
6832 source_name: Some("started".to_string()),
6833 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6834 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6835 name: "netstack".to_string(),
6836 collection: None,
6837 })),
6838 target_name: Some("started".to_string()),
6839 availability: Some(fdecl::Availability::Required),
6840 ..Default::default()
6841 }
6842 ),
6843 fdecl::Offer::EventStream (
6844 fdecl::OfferEventStream {
6845 source_name: Some("stopped".to_string()),
6846 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6847 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
6848 name: "netstack".to_string(),
6849 collection: None,
6850 })),
6851 target_name: Some("some_other_event".to_string()),
6852 availability: Some(fdecl::Availability::Required),
6853 ..Default::default()
6854 }
6855 ),
6856 ]),
6857 capabilities: Some(vec![
6858 fdecl::Capability::Service (
6859 fdecl::Service {
6860 name: Some("svc".into()),
6861 source_path: Some("/svc/svc".into()),
6862 ..Default::default()
6863 },
6864 ),
6865 fdecl::Capability::Storage (
6866 fdecl::Storage {
6867 name: Some("data".to_string()),
6868 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6869 name: "logger".to_string(),
6870 collection: None,
6871 })),
6872 backing_dir: Some("minfs".to_string()),
6873 subdir: None,
6874 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
6875 ..Default::default()
6876 }
6877 )
6878 ]),
6879 children: Some(vec![
6880 fdecl::Child {
6881 name: Some("logger".to_string()),
6882 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
6883 startup: Some(fdecl::StartupMode::Lazy),
6884 environment: None,
6885 on_terminate: None,
6886 ..Default::default()
6887 },
6888 fdecl::Child {
6889 name: Some("netstack".to_string()),
6890 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
6891 startup: Some(fdecl::StartupMode::Lazy),
6892 environment: None,
6893 on_terminate: None,
6894 ..Default::default()
6895 },
6896 ]),
6897 collections: Some(vec![
6898 fdecl::Collection {
6899 name: Some("modular".to_string()),
6900 durability: Some(fdecl::Durability::Transient),
6901 environment: None,
6902 allowed_offers: None,
6903 ..Default::default()
6904 }
6905 ]),
6906 ..default_component_decl()
6907 },
6908 },
6909
6910 test_compile_offer_route_to_dictionary => {
6911 input = json!({
6912 "offer": [
6913 {
6914 "protocol": "A",
6915 "from": "parent/dict/1",
6916 "to": "self/dict",
6917 },
6918 {
6919 "runner": "B",
6920 "from": "#child",
6921 "to": "self/dict",
6922 },
6923 {
6924 "config": "B",
6925 "from": "parent/dict/2",
6926 "to": "self/dict",
6927 "as": "C",
6928 },
6929 ],
6930 "children": [
6931 {
6932 "name": "child",
6933 "url": "fuchsia-pkg://child"
6934 },
6935 ],
6936 "capabilities": [
6937 {
6938 "dictionary": "dict",
6939 },
6940 ],
6941 }),
6942 output = fdecl::Component {
6943 offers: Some(vec![
6944 fdecl::Offer::Protocol (
6945 fdecl::OfferProtocol {
6946 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6947 source_dictionary: Some("dict/1".into()),
6948 source_name: Some("A".into()),
6949 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6950 name: "dict".to_string(),
6951 })),
6952 target_name: Some("A".into()),
6953 dependency_type: Some(fdecl::DependencyType::Strong),
6954 availability: Some(fdecl::Availability::Required),
6955 ..Default::default()
6956 }
6957 ),
6958 fdecl::Offer::Runner (
6959 fdecl::OfferRunner {
6960 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
6961 name: "child".into(),
6962 collection: None,
6963 })),
6964 source_name: Some("B".into()),
6965 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6966 name: "dict".to_string(),
6967 })),
6968 target_name: Some("B".into()),
6969 ..Default::default()
6970 }
6971 ),
6972 fdecl::Offer::Config (
6973 fdecl::OfferConfiguration {
6974 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
6975 source_dictionary: Some("dict/2".into()),
6976 source_name: Some("B".into()),
6977 target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
6978 name: "dict".to_string(),
6979 })),
6980 target_name: Some("C".into()),
6981 availability: Some(fdecl::Availability::Required),
6982 ..Default::default()
6983 }
6984 ),
6985 ]),
6986 capabilities: Some(vec![
6987 fdecl::Capability::Dictionary (
6988 fdecl::Dictionary {
6989 name: Some("dict".into()),
6990 ..Default::default()
6991 }
6992 )
6993 ]),
6994 children: Some(vec![
6995 fdecl::Child {
6996 name: Some("child".to_string()),
6997 url: Some("fuchsia-pkg://child".to_string()),
6998 startup: Some(fdecl::StartupMode::Lazy),
6999 ..Default::default()
7000 },
7001 ]),
7002 ..default_component_decl()
7003 },
7004 },
7005
7006
7007 test_compile_children => {
7008 input = json!({
7009 "children": [
7010 {
7011 "name": "logger",
7012 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
7013 },
7014 {
7015 "name": "gmail",
7016 "url": "https://www.google.com/gmail",
7017 "startup": "eager",
7018 },
7019 {
7020 "name": "echo",
7021 "url": "fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm",
7022 "startup": "lazy",
7023 "on_terminate": "reboot",
7024 "environment": "#myenv",
7025 },
7026 ],
7027 "environments": [
7028 {
7029 "name": "myenv",
7030 "extends": "realm",
7031 },
7032 ],
7033 }),
7034 output = fdecl::Component {
7035 children: Some(vec![
7036 fdecl::Child {
7037 name: Some("logger".to_string()),
7038 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7039 startup: Some(fdecl::StartupMode::Lazy),
7040 environment: None,
7041 on_terminate: None,
7042 ..Default::default()
7043 },
7044 fdecl::Child {
7045 name: Some("gmail".to_string()),
7046 url: Some("https://www.google.com/gmail".to_string()),
7047 startup: Some(fdecl::StartupMode::Eager),
7048 environment: None,
7049 on_terminate: None,
7050 ..Default::default()
7051 },
7052 fdecl::Child {
7053 name: Some("echo".to_string()),
7054 url: Some("fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm".to_string()),
7055 startup: Some(fdecl::StartupMode::Lazy),
7056 environment: Some("myenv".to_string()),
7057 on_terminate: Some(fdecl::OnTerminate::Reboot),
7058 ..Default::default()
7059 }
7060 ]),
7061 environments: Some(vec![
7062 fdecl::Environment {
7063 name: Some("myenv".to_string()),
7064 extends: Some(fdecl::EnvironmentExtends::Realm),
7065 runners: None,
7066 resolvers: None,
7067 stop_timeout_ms: None,
7068 ..Default::default()
7069 }
7070 ]),
7071 ..default_component_decl()
7072 },
7073 },
7074
7075 test_compile_collections => {
7076 input = json!({
7077 "collections": [
7078 {
7079 "name": "modular",
7080 "durability": "single_run",
7081 },
7082 {
7083 "name": "tests",
7084 "durability": "transient",
7085 "environment": "#myenv",
7086 },
7087 ],
7088 "environments": [
7089 {
7090 "name": "myenv",
7091 "extends": "realm",
7092 }
7093 ],
7094 }),
7095 output = fdecl::Component {
7096 collections: Some(vec![
7097 fdecl::Collection {
7098 name: Some("modular".to_string()),
7099 durability: Some(fdecl::Durability::SingleRun),
7100 environment: None,
7101 allowed_offers: None,
7102 ..Default::default()
7103 },
7104 fdecl::Collection {
7105 name: Some("tests".to_string()),
7106 durability: Some(fdecl::Durability::Transient),
7107 environment: Some("myenv".to_string()),
7108 allowed_offers: None,
7109 ..Default::default()
7110 }
7111 ]),
7112 environments: Some(vec![
7113 fdecl::Environment {
7114 name: Some("myenv".to_string()),
7115 extends: Some(fdecl::EnvironmentExtends::Realm),
7116 runners: None,
7117 resolvers: None,
7118 stop_timeout_ms: None,
7119 ..Default::default()
7120 }
7121 ]),
7122 ..default_component_decl()
7123 },
7124 },
7125
7126 test_compile_capabilities => {
7127 features = FeatureSet::from(vec![Feature::DynamicDictionaries]),
7128 input = json!({
7129 "capabilities": [
7130 {
7131 "protocol": "myprotocol",
7132 "path": "/protocol",
7133 },
7134 {
7135 "protocol": "myprotocol2",
7136 },
7137 {
7138 "protocol": [ "myprotocol3", "myprotocol4" ],
7139 },
7140 {
7141 "directory": "mydirectory",
7142 "path": "/directory",
7143 "rights": [ "connect" ],
7144 },
7145 {
7146 "storage": "mystorage",
7147 "backing_dir": "storage",
7148 "from": "#minfs",
7149 "storage_id": "static_instance_id_or_moniker",
7150 },
7151 {
7152 "storage": "mystorage2",
7153 "backing_dir": "storage2",
7154 "from": "#minfs",
7155 "storage_id": "static_instance_id",
7156 },
7157 {
7158 "runner": "myrunner",
7159 "path": "/runner",
7160 },
7161 {
7162 "resolver": "myresolver",
7163 "path": "/resolver"
7164 },
7165 {
7166 "dictionary": "dict1",
7167 },
7168 {
7169 "dictionary": "dict2",
7170 "path": "/in/a",
7171 },
7172 ],
7173 "children": [
7174 {
7175 "name": "minfs",
7176 "url": "fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm",
7177 },
7178 ]
7179 }),
7180 output = fdecl::Component {
7181 capabilities: Some(vec![
7182 fdecl::Capability::Protocol (
7183 fdecl::Protocol {
7184 name: Some("myprotocol".to_string()),
7185 source_path: Some("/protocol".to_string()),
7186 ..Default::default()
7187 }
7188 ),
7189 fdecl::Capability::Protocol (
7190 fdecl::Protocol {
7191 name: Some("myprotocol2".to_string()),
7192 source_path: Some("/svc/myprotocol2".to_string()),
7193 ..Default::default()
7194 }
7195 ),
7196 fdecl::Capability::Protocol (
7197 fdecl::Protocol {
7198 name: Some("myprotocol3".to_string()),
7199 source_path: Some("/svc/myprotocol3".to_string()),
7200 ..Default::default()
7201 }
7202 ),
7203 fdecl::Capability::Protocol (
7204 fdecl::Protocol {
7205 name: Some("myprotocol4".to_string()),
7206 source_path: Some("/svc/myprotocol4".to_string()),
7207 ..Default::default()
7208 }
7209 ),
7210 fdecl::Capability::Directory (
7211 fdecl::Directory {
7212 name: Some("mydirectory".to_string()),
7213 source_path: Some("/directory".to_string()),
7214 rights: Some(fio::Operations::CONNECT),
7215 ..Default::default()
7216 }
7217 ),
7218 fdecl::Capability::Storage (
7219 fdecl::Storage {
7220 name: Some("mystorage".to_string()),
7221 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7222 name: "minfs".to_string(),
7223 collection: None,
7224 })),
7225 backing_dir: Some("storage".to_string()),
7226 subdir: None,
7227 storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
7228 ..Default::default()
7229 }
7230 ),
7231 fdecl::Capability::Storage (
7232 fdecl::Storage {
7233 name: Some("mystorage2".to_string()),
7234 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7235 name: "minfs".to_string(),
7236 collection: None,
7237 })),
7238 backing_dir: Some("storage2".to_string()),
7239 subdir: None,
7240 storage_id: Some(fdecl::StorageId::StaticInstanceId),
7241 ..Default::default()
7242 }
7243 ),
7244 fdecl::Capability::Runner (
7245 fdecl::Runner {
7246 name: Some("myrunner".to_string()),
7247 source_path: Some("/runner".to_string()),
7248 ..Default::default()
7249 }
7250 ),
7251 fdecl::Capability::Resolver (
7252 fdecl::Resolver {
7253 name: Some("myresolver".to_string()),
7254 source_path: Some("/resolver".to_string()),
7255 ..Default::default()
7256 }
7257 ),
7258 fdecl::Capability::Dictionary (
7259 fdecl::Dictionary {
7260 name: Some("dict1".into()),
7261 ..Default::default()
7262 }
7263 ),
7264 fdecl::Capability::Dictionary (
7265 fdecl::Dictionary {
7266 name: Some("dict2".into()),
7267 source: None,
7268 source_dictionary: None,
7269 source_path: Some("/in/a".into()),
7270 ..Default::default()
7271 }
7272 ),
7273 ]),
7274 children: Some(vec![
7275 fdecl::Child {
7276 name: Some("minfs".to_string()),
7277 url: Some("fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm".to_string()),
7278 startup: Some(fdecl::StartupMode::Lazy),
7279 environment: None,
7280 on_terminate: None,
7281 ..Default::default()
7282 }
7283 ]),
7284 ..default_component_decl()
7285 },
7286 },
7287
7288 test_compile_facets => {
7289 input = json!({
7290 "facets": {
7291 "title": "foo",
7292 "authors": [ "me", "you" ],
7293 "year": "2018",
7294 "metadata": {
7295 "publisher": "The Books Publisher",
7296 }
7297 }
7298 }),
7299 output = fdecl::Component {
7300 facets: Some(fdata::Dictionary {
7301 entries: Some(vec![
7302 fdata::DictionaryEntry {
7303 key: "authors".to_string(),
7304 value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["me".to_owned(), "you".to_owned()]))),
7305 },
7306 fdata::DictionaryEntry {
7307 key: "metadata.publisher".to_string(),
7308 value: Some(Box::new(fdata::DictionaryValue::Str("The Books Publisher".to_string()))),
7309 },
7310 fdata::DictionaryEntry {
7311 key: "title".to_string(),
7312 value: Some(Box::new(fdata::DictionaryValue::Str("foo".to_string()))),
7313 },
7314 fdata::DictionaryEntry {
7315 key: "year".to_string(),
7316 value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
7317 },
7318 ]),
7319 ..Default::default()
7320 }
7321 ),
7322 ..default_component_decl()
7323 },
7324 },
7325
7326 test_compile_environment => {
7327 input = json!({
7328 "environments": [
7329 {
7330 "name": "myenv",
7331 "__stop_timeout_ms": 10u32,
7332 },
7333 {
7334 "name": "myenv2",
7335 "extends": "realm",
7336 },
7337 {
7338 "name": "myenv3",
7339 "extends": "none",
7340 "__stop_timeout_ms": 8000u32,
7341 }
7342 ],
7343 }),
7344 output = fdecl::Component {
7345 environments: Some(vec![
7346 fdecl::Environment {
7347 name: Some("myenv".to_string()),
7348 extends: Some(fdecl::EnvironmentExtends::None),
7349 runners: None,
7350 resolvers: None,
7351 stop_timeout_ms: Some(10),
7352 ..Default::default()
7353 },
7354 fdecl::Environment {
7355 name: Some("myenv2".to_string()),
7356 extends: Some(fdecl::EnvironmentExtends::Realm),
7357 runners: None,
7358 resolvers: None,
7359 stop_timeout_ms: None,
7360 ..Default::default()
7361 },
7362 fdecl::Environment {
7363 name: Some("myenv3".to_string()),
7364 extends: Some(fdecl::EnvironmentExtends::None),
7365 runners: None,
7366 resolvers: None,
7367 stop_timeout_ms: Some(8000),
7368 ..Default::default()
7369 },
7370 ]),
7371 ..default_component_decl()
7372 },
7373 },
7374
7375 test_compile_environment_with_runner_and_resolver => {
7376 input = json!({
7377 "environments": [
7378 {
7379 "name": "myenv",
7380 "extends": "realm",
7381 "runners": [
7382 {
7383 "runner": "dart",
7384 "from": "parent",
7385 }
7386 ],
7387 "resolvers": [
7388 {
7389 "resolver": "pkg_resolver",
7390 "from": "parent",
7391 "scheme": "fuchsia-pkg",
7392 }
7393 ],
7394 },
7395 ],
7396 }),
7397 output = fdecl::Component {
7398 environments: Some(vec![
7399 fdecl::Environment {
7400 name: Some("myenv".to_string()),
7401 extends: Some(fdecl::EnvironmentExtends::Realm),
7402 runners: Some(vec![
7403 fdecl::RunnerRegistration {
7404 source_name: Some("dart".to_string()),
7405 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7406 target_name: Some("dart".to_string()),
7407 ..Default::default()
7408 }
7409 ]),
7410 resolvers: Some(vec![
7411 fdecl::ResolverRegistration {
7412 resolver: Some("pkg_resolver".to_string()),
7413 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7414 scheme: Some("fuchsia-pkg".to_string()),
7415 ..Default::default()
7416 }
7417 ]),
7418 stop_timeout_ms: None,
7419 ..Default::default()
7420 },
7421 ]),
7422 ..default_component_decl()
7423 },
7424 },
7425
7426 test_compile_environment_with_runner_alias => {
7427 input = json!({
7428 "environments": [
7429 {
7430 "name": "myenv",
7431 "extends": "realm",
7432 "runners": [
7433 {
7434 "runner": "dart",
7435 "from": "parent",
7436 "as": "my-dart",
7437 }
7438 ],
7439 },
7440 ],
7441 }),
7442 output = fdecl::Component {
7443 environments: Some(vec![
7444 fdecl::Environment {
7445 name: Some("myenv".to_string()),
7446 extends: Some(fdecl::EnvironmentExtends::Realm),
7447 runners: Some(vec![
7448 fdecl::RunnerRegistration {
7449 source_name: Some("dart".to_string()),
7450 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7451 target_name: Some("my-dart".to_string()),
7452 ..Default::default()
7453 }
7454 ]),
7455 resolvers: None,
7456 stop_timeout_ms: None,
7457 ..Default::default()
7458 },
7459 ]),
7460 ..default_component_decl()
7461 },
7462 },
7463
7464 test_compile_environment_with_debug => {
7465 input = json!({
7466 "capabilities": [
7467 {
7468 "protocol": "fuchsia.serve.service",
7469 },
7470 ],
7471 "environments": [
7472 {
7473 "name": "myenv",
7474 "extends": "realm",
7475 "debug": [
7476 {
7477 "protocol": "fuchsia.serve.service",
7478 "from": "self",
7479 "as": "my-service",
7480 }
7481 ],
7482 },
7483 ],
7484 }),
7485 output = fdecl::Component {
7486 capabilities: Some(vec![
7487 fdecl::Capability::Protocol(
7488 fdecl::Protocol {
7489 name : Some("fuchsia.serve.service".to_owned()),
7490 source_path: Some("/svc/fuchsia.serve.service".to_owned()),
7491 ..Default::default()
7492 }
7493 )
7494 ]),
7495 environments: Some(vec![
7496 fdecl::Environment {
7497 name: Some("myenv".to_string()),
7498 extends: Some(fdecl::EnvironmentExtends::Realm),
7499 debug_capabilities: Some(vec![
7500 fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
7501 source_name: Some("fuchsia.serve.service".to_string()),
7502 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
7503 target_name: Some("my-service".to_string()),
7504 ..Default::default()
7505 }),
7506 ]),
7507 resolvers: None,
7508 runners: None,
7509 stop_timeout_ms: None,
7510 ..Default::default()
7511 },
7512 ]),
7513 ..default_component_decl()
7514 },
7515 },
7516
7517
7518 test_compile_configuration_capability => {
7519 input = json!({
7520 "capabilities": [
7521 {
7522 "config": "fuchsia.config.true",
7523 "type": "bool",
7524 "value": true,
7525 },
7526 {
7527 "config": "fuchsia.config.false",
7528 "type": "bool",
7529 "value": false,
7530 },
7531 ],
7532 }),
7533 output = fdecl::Component {
7534 capabilities: Some(vec![
7535 fdecl::Capability::Config (
7536 fdecl::Configuration {
7537 name: Some("fuchsia.config.true".to_string()),
7538 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
7539 ..Default::default()
7540 }),
7541 fdecl::Capability::Config (
7542 fdecl::Configuration {
7543 name: Some("fuchsia.config.false".to_string()),
7544 value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(false))),
7545 ..Default::default()
7546 }),
7547 ]),
7548 ..default_component_decl()
7549 },
7550 },
7551
7552 test_compile_all_sections => {
7553 input = json!({
7554 "program": {
7555 "runner": "elf",
7556 "binary": "bin/app",
7557 },
7558 "use": [
7559 { "protocol": "LegacyCoolFonts", "path": "/svc/fuchsia.fonts.LegacyProvider" },
7560 { "protocol": [ "ReallyGoodFonts", "IWouldNeverUseTheseFonts"]},
7561 { "protocol": "DebugProtocol", "from": "debug"},
7562 ],
7563 "expose": [
7564 { "directory": "blobfs", "from": "self", "rights": ["r*"]},
7565 ],
7566 "offer": [
7567 {
7568 "protocol": "fuchsia.logger.LegacyLog",
7569 "from": "#logger",
7570 "to": [ "#netstack", "#modular" ],
7571 "dependency": "weak"
7572 },
7573 ],
7574 "children": [
7575 {
7576 "name": "logger",
7577 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
7578 },
7579 {
7580 "name": "netstack",
7581 "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm",
7582 },
7583 ],
7584 "collections": [
7585 {
7586 "name": "modular",
7587 "durability": "transient",
7588 },
7589 ],
7590 "capabilities": [
7591 {
7592 "directory": "blobfs",
7593 "path": "/volumes/blobfs",
7594 "rights": [ "r*" ],
7595 },
7596 {
7597 "runner": "myrunner",
7598 "path": "/runner",
7599 },
7600 {
7601 "protocol": "fuchsia.serve.service",
7602 }
7603 ],
7604 "facets": {
7605 "author": "Fuchsia",
7606 "year": "2018",
7607 },
7608 "environments": [
7609 {
7610 "name": "myenv",
7611 "extends": "realm",
7612 "debug": [
7613 {
7614 "protocol": "fuchsia.serve.service",
7615 "from": "self",
7616 "as": "my-service",
7617 },
7618 {
7619 "protocol": "fuchsia.logger.LegacyLog",
7620 "from": "#logger",
7621 }
7622 ]
7623 }
7624 ],
7625 }),
7626 output = fdecl::Component {
7627 program: Some(fdecl::Program {
7628 runner: Some("elf".to_string()),
7629 info: Some(fdata::Dictionary {
7630 entries: Some(vec![fdata::DictionaryEntry {
7631 key: "binary".to_string(),
7632 value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
7633 }]),
7634 ..Default::default()
7635 }),
7636 ..Default::default()
7637 }),
7638 uses: Some(vec![
7639 fdecl::Use::Protocol (
7640 fdecl::UseProtocol {
7641 dependency_type: Some(fdecl::DependencyType::Strong),
7642 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7643 source_name: Some("LegacyCoolFonts".to_string()),
7644 target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
7645 availability: Some(fdecl::Availability::Required),
7646 ..Default::default()
7647 }
7648 ),
7649 fdecl::Use::Protocol (
7650 fdecl::UseProtocol {
7651 dependency_type: Some(fdecl::DependencyType::Strong),
7652 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7653 source_name: Some("ReallyGoodFonts".to_string()),
7654 target_path: Some("/svc/ReallyGoodFonts".to_string()),
7655 availability: Some(fdecl::Availability::Required),
7656 ..Default::default()
7657 }
7658 ),
7659 fdecl::Use::Protocol (
7660 fdecl::UseProtocol {
7661 dependency_type: Some(fdecl::DependencyType::Strong),
7662 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7663 source_name: Some("IWouldNeverUseTheseFonts".to_string()),
7664 target_path: Some("/svc/IWouldNeverUseTheseFonts".to_string()),
7665 availability: Some(fdecl::Availability::Required),
7666 ..Default::default()
7667 }
7668 ),
7669 fdecl::Use::Protocol (
7670 fdecl::UseProtocol {
7671 dependency_type: Some(fdecl::DependencyType::Strong),
7672 source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
7673 source_name: Some("DebugProtocol".to_string()),
7674 target_path: Some("/svc/DebugProtocol".to_string()),
7675 availability: Some(fdecl::Availability::Required),
7676 ..Default::default()
7677 }
7678 ),
7679 ]),
7680 exposes: Some(vec![
7681 fdecl::Expose::Directory (
7682 fdecl::ExposeDirectory {
7683 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
7684 source_name: Some("blobfs".to_string()),
7685 target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
7686 target_name: Some("blobfs".to_string()),
7687 rights: Some(
7688 fio::Operations::CONNECT | fio::Operations::ENUMERATE |
7689 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
7690 fio::Operations::GET_ATTRIBUTES
7691 ),
7692 subdir: None,
7693 availability: Some(fdecl::Availability::Required),
7694 ..Default::default()
7695 }
7696 ),
7697 ]),
7698 offers: Some(vec![
7699 fdecl::Offer::Protocol (
7700 fdecl::OfferProtocol {
7701 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7702 name: "logger".to_string(),
7703 collection: None,
7704 })),
7705 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7706 target: Some(fdecl::Ref::Child(fdecl::ChildRef {
7707 name: "netstack".to_string(),
7708 collection: None,
7709 })),
7710 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7711 dependency_type: Some(fdecl::DependencyType::Weak),
7712 availability: Some(fdecl::Availability::Required),
7713 ..Default::default()
7714 }
7715 ),
7716 fdecl::Offer::Protocol (
7717 fdecl::OfferProtocol {
7718 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7719 name: "logger".to_string(),
7720 collection: None,
7721 })),
7722 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7723 target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
7724 name: "modular".to_string(),
7725 })),
7726 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7727 dependency_type: Some(fdecl::DependencyType::Weak),
7728 availability: Some(fdecl::Availability::Required),
7729 ..Default::default()
7730 }
7731 ),
7732 ]),
7733 capabilities: Some(vec![
7734 fdecl::Capability::Directory (
7735 fdecl::Directory {
7736 name: Some("blobfs".to_string()),
7737 source_path: Some("/volumes/blobfs".to_string()),
7738 rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
7739 fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
7740 fio::Operations::GET_ATTRIBUTES
7741 ),
7742 ..Default::default()
7743 }
7744 ),
7745 fdecl::Capability::Runner (
7746 fdecl::Runner {
7747 name: Some("myrunner".to_string()),
7748 source_path: Some("/runner".to_string()),
7749 ..Default::default()
7750 }
7751 ),
7752 fdecl::Capability::Protocol(
7753 fdecl::Protocol {
7754 name : Some("fuchsia.serve.service".to_owned()),
7755 source_path: Some("/svc/fuchsia.serve.service".to_owned()),
7756 ..Default::default()
7757 }
7758 )
7759 ]),
7760 children: Some(vec![
7761 fdecl::Child {
7762 name: Some("logger".to_string()),
7763 url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
7764 startup: Some(fdecl::StartupMode::Lazy),
7765 environment: None,
7766 on_terminate: None,
7767 ..Default::default()
7768 },
7769 fdecl::Child {
7770 name: Some("netstack".to_string()),
7771 url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
7772 startup: Some(fdecl::StartupMode::Lazy),
7773 environment: None,
7774 on_terminate: None,
7775 ..Default::default()
7776 },
7777 ]),
7778 collections: Some(vec![
7779 fdecl::Collection {
7780 name: Some("modular".to_string()),
7781 durability: Some(fdecl::Durability::Transient),
7782 environment: None,
7783 allowed_offers: None,
7784 ..Default::default()
7785 }
7786 ]),
7787 environments: Some(vec![
7788 fdecl::Environment {
7789 name: Some("myenv".to_string()),
7790 extends: Some(fdecl::EnvironmentExtends::Realm),
7791 runners: None,
7792 resolvers: None,
7793 stop_timeout_ms: None,
7794 debug_capabilities: Some(vec![
7795 fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
7796 source_name: Some("fuchsia.serve.service".to_string()),
7797 source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
7798 target_name: Some("my-service".to_string()),
7799 ..Default::default()
7800 }),
7801 fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
7802 source_name: Some("fuchsia.logger.LegacyLog".to_string()),
7803 source: Some(fdecl::Ref::Child(fdecl::ChildRef {
7804 name: "logger".to_string(),
7805 collection: None,
7806 })),
7807 target_name: Some("fuchsia.logger.LegacyLog".to_string()),
7808 ..Default::default()
7809 }),
7810 ]),
7811 ..Default::default()
7812 }
7813 ]),
7814 facets: Some(fdata::Dictionary {
7815 entries: Some(vec![
7816 fdata::DictionaryEntry {
7817 key: "author".to_string(),
7818 value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
7819 },
7820 fdata::DictionaryEntry {
7821 key: "year".to_string(),
7822 value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
7823 },
7824 ]),
7825 ..Default::default()
7826 }),
7827 ..Default::default()
7828 },
7829 },
7830 }
7831
7832 #[test]
7833 fn test_maybe_generate_specialization_from_all() {
7834 let offer = create_offer(
7835 "fuchsia.logger.LegacyLog",
7836 OneOrMany::One(OfferFromRef::Parent {}),
7837 OneOrMany::One(OfferToRef::All),
7838 );
7839
7840 let mut offer_set = vec![create_offer(
7841 "fuchsia.logger.LogSink",
7842 OneOrMany::One(OfferFromRef::Parent {}),
7843 OneOrMany::One(OfferToRef::All),
7844 )];
7845
7846 let result = maybe_generate_direct_offer_from_all_deprecated(
7847 &offer,
7848 &offer_set,
7849 &Name::from_str("something").unwrap(),
7850 );
7851
7852 assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
7853 assert_eq!(
7854 protocol,
7855 &Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
7856 );
7857 assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
7858 assert_eq!(
7859 to,
7860 &OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7861 );
7862 });
7863
7864 offer_set.push(create_offer(
7865 "fuchsia.inspect.InspectSink",
7866 OneOrMany::One(OfferFromRef::Parent {}),
7867 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7868 ));
7869
7870 let result = maybe_generate_direct_offer_from_all_deprecated(
7871 &offer,
7872 &offer_set,
7873 &Name::from_str("something").unwrap(),
7874 );
7875
7876 assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
7877 assert_eq!(
7878 protocol,
7879 &Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
7880 );
7881 assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
7882 assert_eq!(
7883 to,
7884 &OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7885 );
7886 });
7887
7888 offer_set.push(create_offer(
7889 "fuchsia.logger.LegacyLog",
7890 OneOrMany::One(OfferFromRef::Parent {}),
7891 OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
7892 ));
7893
7894 assert!(
7895 maybe_generate_direct_offer_from_all_deprecated(
7896 &offer,
7897 &offer_set,
7898 &Name::from_str("something").unwrap()
7899 )
7900 .is_empty()
7901 );
7902 }
7903
7904 #[test]
7905 fn test_expose_void_service_capability() {
7906 let input = must_parse_cml!({
7907 "expose": [
7908 {
7909 "service": "fuchsia.foo.Bar",
7910 "from": [ "#non_existent_child" ],
7911 "source_availability": "unknown",
7912 },
7913 ],
7914 });
7915 let result = compile(&input, CompileOptions::default());
7916 assert_matches!(result, Ok(_));
7917 }
7918
7919 #[test]
7921 fn test_aggregated_capabilities_must_use_same_availability_expose() {
7922 let input = must_parse_cml!({
7924 "expose": [
7925 {
7926 "service": "fuchsia.foo.Bar",
7927 "from": [ "#a", "#b" ],
7928 "availability": "optional",
7929 },
7930 ],
7931 "collections": [
7932 {
7933 "name": "a",
7934 "durability": "transient",
7935 },
7936 {
7937 "name": "b",
7938 "durability": "transient",
7939 },
7940 ],
7941 });
7942 let result = compile(&input, CompileOptions::default());
7943 assert_matches!(result, Ok(_));
7944
7945 let input = must_parse_cml!({
7947 "expose": [
7948 {
7949 "service": "fuchsia.foo.Bar",
7950 "from": [ "#a", "#non_existent" ],
7951 "source_availability": "unknown",
7952 },
7953 ],
7954 "collections": [
7955 {
7956 "name": "a",
7957 "durability": "transient",
7958 },
7959 ],
7960 });
7961 let result = compile(&input, CompileOptions::default());
7962 assert_matches!(
7963 result,
7964 Err(Error::FidlValidator { errs: ErrorList { errs } })
7965 if matches!(
7966 &errs[..],
7967 [
7968 CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
7969 ]
7970 if matches!(
7971 &availabilities[..],
7972 [ fdecl::Availability::Required, fdecl::Availability::Optional, ]
7973 )
7974 )
7975 );
7976 }
7977
7978 #[test]
7979 fn test_aggregated_capabilities_must_use_same_availability_offer() {
7980 let input = must_parse_cml!({
7982 "offer": [
7983 {
7984 "service": "fuchsia.foo.Bar",
7985 "from": [ "#a", "#b" ],
7986 "to": "#c",
7987 "availability": "optional",
7988 },
7989 ],
7990 "collections": [
7991 {
7992 "name": "a",
7993 "durability": "transient",
7994 },
7995 {
7996 "name": "b",
7997 "durability": "transient",
7998 },
7999 ],
8000 "children": [
8001 {
8002 "name": "c",
8003 "url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
8004 },
8005 ],
8006 });
8007 let result = compile(&input, CompileOptions::default());
8008 assert_matches!(result, Ok(_));
8009
8010 let input = must_parse_cml!({
8012 "offer": [
8013 {
8014 "service": "fuchsia.foo.Bar",
8015 "from": [ "#a", "#non_existent" ],
8016 "to": "#c",
8017 "source_availability": "unknown",
8018 },
8019 ],
8020 "collections": [
8021 {
8022 "name": "a",
8023 "durability": "transient",
8024 },
8025 ],
8026 "children": [
8027 {
8028 "name": "c",
8029 "url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
8030 },
8031 ],
8032 });
8033 let result = compile(&input, CompileOptions::default());
8034 assert_matches!(
8035 result,
8036 Err(Error::FidlValidator { errs: ErrorList { errs } })
8037 if matches!(
8038 &errs[..],
8039 [
8040 CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
8041 ]
8042 if matches!(
8043 &availabilities[..],
8044 [ fdecl::Availability::Required, fdecl::Availability::Optional, ]
8045 )
8046 )
8047 );
8048 }
8049
8050 #[test]
8051 fn test_compile_offer_to_all_exact_duplicate_disallowed() {
8052 let input = must_parse_cml!({
8053 "children": [
8054 {
8055 "name": "logger",
8056 "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
8057 },
8058 ],
8059 "offer": [
8060 {
8061 "protocol": "fuchsia.logger.LogSink",
8062 "from": "parent",
8063 "to": "all",
8064 },
8065 {
8066 "protocol": "fuchsia.logger.LogSink",
8067 "from": "parent",
8068 "to": "all",
8069 },
8070 ],
8071 });
8072 assert_matches!(
8073 compile(&input, CompileOptions::default()),
8074 Err(Error::Validate { err, .. })
8075 if &err == "Protocol(s) [\"fuchsia.logger.LogSink\"] offered to \"all\" multiple times"
8076 );
8077 }
8078
8079 #[test]
8080 fn test_compile_use_config() {
8081 let input = must_parse_cml!({
8082 "use": [
8083 {
8084 "config": "fuchsia.config.Config",
8085 "key" : "my_config",
8086 "type": "bool",
8087 }
8088 ],
8089 });
8090 let options = CompileOptions::new().config_package_path("fake.cvf");
8091 let actual = compile(&input, options).unwrap();
8092 let type_ = fdecl::ConfigType {
8093 layout: fdecl::ConfigTypeLayout::Bool,
8094 parameters: Some(vec![]),
8095 constraints: vec![],
8096 };
8097 assert_eq!(
8098 actual.uses.unwrap(),
8099 vec![fdecl::Use::Config(fdecl::UseConfiguration {
8100 source_name: Some("fuchsia.config.Config".to_string()),
8101 source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
8102 target_name: Some("my_config".to_string()),
8103 availability: Some(fdecl::Availability::Required),
8104 type_: Some(type_.clone()),
8105 ..Default::default()
8106 })]
8107 );
8108 assert_eq!(
8109 actual.config.unwrap().fields.unwrap(),
8110 [fdecl::ConfigField {
8111 key: Some("my_config".to_string()),
8112 type_: Some(type_),
8113 mutability: Some(fdecl::ConfigMutability::default()),
8114 ..Default::default()
8115 }]
8116 .to_vec(),
8117 );
8118 }
8119
8120 #[test]
8121 fn test_compile_use_config_optional_bad_type() {
8122 let input = must_parse_cml!({
8123 "use": [
8124 {
8125 "config": "fuchsia.config.Config",
8126 "key" : "my_config",
8127 "type": "bool",
8128 "availability": "optional",
8129 }
8130 ],
8131 "config": {
8132 "my_config": { "type": "int8"},
8133 }
8134 });
8135 let options = CompileOptions::new().config_package_path("fake.cvf");
8136 assert_matches!(
8137 compile(&input, options),
8138 Err(Error::Validate { err, .. })
8139 if &err == "Use and config block differ on type for key 'my_config'"
8140 );
8141 }
8142
8143 #[test]
8144 fn test_config_source_from_package() {
8145 let input = must_parse_cml!({
8146 "use": [
8147 {
8148 "config": "fuchsia.config.Config",
8149 "key" : "my_config",
8150 "type": "bool",
8151 "availability": "optional",
8152 }
8153 ],
8154 "config": {
8155 "my_config": { "type": "bool"},
8156 }
8157 });
8158 let options = CompileOptions::new().config_package_path("fake.cvf");
8159 let decl = compile(&input, options).unwrap();
8160 let config = decl.config.unwrap();
8161 assert_eq!(
8162 config.value_source,
8163 Some(fdecl::ConfigValueSource::PackagePath("fake.cvf".into()))
8164 );
8165 }
8166
8167 #[test]
8168 fn test_config_source_from_capabilities() {
8169 let input = must_parse_cml!({
8170 "use": [
8171 {
8172 "config": "fuchsia.config.Config",
8173 "key" : "my_config",
8174 "type": "bool",
8175 }
8176 ],
8177 });
8178 let options = CompileOptions::new().config_package_path("fake.cvf");
8179 let decl = compile(&input, options).unwrap();
8180 let config = decl.config.unwrap();
8181 assert_eq!(
8182 config.value_source,
8183 Some(
8184 fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default())
8185 )
8186 );
8187 }
8188
8189 #[test]
8190 fn test_config_default() {
8191 let input = must_parse_cml!({
8192 "use": [
8193 {
8194 "config": "fuchsia.config.Config",
8195 "key" : "my_config",
8196 "type": "bool",
8197 "availability": "optional",
8198 "default": true
8199 }
8200 ],
8201 });
8202 let options = CompileOptions::new().config_package_path("fake.cvf");
8203 let decl = compile(&input, options).unwrap();
8204 assert_matches!(
8205 decl.uses.as_ref().unwrap()[0],
8206 fdecl::Use::Config(fdecl::UseConfiguration {
8207 default: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
8208 ..
8209 })
8210 );
8211 }
8212
8213 #[test]
8214 fn test_config_default_bad_type() {
8215 let input = must_parse_cml!({
8216 "use": [
8217 {
8218 "config": "fuchsia.config.Config",
8219 "key" : "my_config",
8220 "type": "bool",
8221 "availability": "optional",
8222 "default": 5
8223 }
8224 ],
8225 });
8226 let options = CompileOptions::new().config_package_path("fake.cvf");
8227 assert_matches!(compile(&input, options), Err(Error::InvalidArgs(_)));
8228 }
8229
8230 #[test]
8231 fn test_compile_protocol_delivery_type() {
8232 let input = must_parse_cml!({
8233 "capabilities": [
8234 {
8235 "protocol": "fuchsia.echo.Echo",
8236 "delivery": "on_readable",
8237 }
8238 ],
8239 });
8240 let features = FeatureSet::from(vec![Feature::DeliveryType]);
8241 let options = CompileOptions::new().features(&features);
8242 let decl = compile(&input, options).unwrap();
8243 assert_matches!(
8244 decl.capabilities.as_ref().unwrap()[0],
8245 fdecl::Capability::Protocol(fdecl::Protocol {
8246 delivery: Some(fdecl::DeliveryType::OnReadable),
8247 ..
8248 })
8249 );
8250 }
8251
8252 #[test]
8253 fn test_compile_protocol_setting_delivery_type_requires_feature_flag() {
8254 let input = must_parse_cml!({
8255 "capabilities": [
8256 {
8257 "protocol": "fuchsia.echo.Echo",
8258 "delivery": "on_readable",
8259 }
8260 ],
8261 });
8262 assert_matches!(
8263 compile(&input, CompileOptions::new()),
8264 Err(Error::RestrictedFeature(feature))
8265 if feature == "delivery_type"
8266 );
8267 }
8268}