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