| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use crate::cml::{self, CapabilityClause}; |
| use crate::error::Error; |
| use crate::one_or_many::OneOrMany; |
| use crate::translate; |
| use crate::util::write_depfile; |
| use crate::validate; |
| use cm_types as cm; |
| use fidl::encoding::encode_persistent; |
| use fidl_fuchsia_data as fdata; |
| use fidl_fuchsia_io2 as fio2; |
| use fidl_fuchsia_sys2 as fsys; |
| use serde_json::{self, value::Value, Map}; |
| use std::collections::HashSet; |
| use std::fs; |
| use std::io::Write; |
| use std::path::PathBuf; |
| |
| /// Read in a CML file and produce the equivalent CM. |
| pub fn compile( |
| file: &PathBuf, |
| output: &PathBuf, |
| depfile: Option<PathBuf>, |
| includepath: PathBuf, |
| ) -> Result<(), Error> { |
| match file.extension().and_then(|e| e.to_str()) { |
| Some("cml") => Ok(()), |
| _ => Err(Error::invalid_args(format!( |
| "Input file {:?} does not have the component manifest language extension (.cml)", |
| file |
| ))), |
| }?; |
| match output.extension().and_then(|e| e.to_str()) { |
| Some("cm") => Ok(()), |
| _ => Err(Error::invalid_args(format!( |
| "Output file {:?} does not have the component manifest extension (.cm)", |
| output |
| ))), |
| }?; |
| |
| let document = validate::parse_cml(file.as_path(), Some(&includepath))?; |
| let mut out_data = compile_cml(&document)?; |
| |
| let mut out_file = |
| fs::OpenOptions::new().create(true).truncate(true).write(true).open(output)?; |
| out_file.write(&encode_persistent(&mut out_data)?)?; |
| |
| // Write includes to depfile |
| if let Some(depfile_path) = depfile { |
| write_depfile( |
| &depfile_path, |
| Some(&output.to_path_buf()), |
| &document.includes(), |
| &includepath, |
| )?; |
| } |
| |
| Ok(()) |
| } |
| |
| fn compile_cml(document: &cml::Document) -> Result<fsys::ComponentDecl, Error> { |
| let all_capability_names = document.all_capability_names(); |
| Ok(fsys::ComponentDecl { |
| program: document.program.as_ref().map(translate_program).transpose()?, |
| uses: document.r#use.as_ref().map(translate_use).transpose()?, |
| exposes: document |
| .expose |
| .as_ref() |
| .map(|e| translate_expose(e, &all_capability_names)) |
| .transpose()?, |
| offers: document |
| .offer |
| .as_ref() |
| .map(|offer| { |
| let all_children = document.all_children_names().into_iter().collect(); |
| let all_collections = document.all_collection_names().into_iter().collect(); |
| translate_offer(offer, &all_children, &all_collections, &all_capability_names) |
| }) |
| .transpose()?, |
| capabilities: document |
| .capabilities |
| .as_ref() |
| .map(translate::translate_capabilities) |
| .transpose()?, |
| children: document.children.as_ref().map(translate_children).transpose()?, |
| collections: document.collections.as_ref().map(translate_collections).transpose()?, |
| environments: document.environments.as_ref().map(translate_environments).transpose()?, |
| facets: document.facets.clone().map(fsys_object_from_map).transpose()?, |
| ..fsys::ComponentDecl::EMPTY |
| }) |
| } |
| |
| // Converts a Map<String, serde_json::Value> to a fuchsia Object. |
| fn fsys_object_from_map(dictionary: Map<String, Value>) -> Result<fsys::Object, Error> { |
| let mut out = fsys::Object { entries: vec![] }; |
| for (k, v) in dictionary { |
| if let Some(value) = convert_value(v)? { |
| out.entries.push(fsys::Entry { key: k, value: Some(value) }); |
| } |
| } |
| Ok(out) |
| } |
| |
| // Converts a serde_json::Value into a fuchsia fidl Value. Used by `fsys_object_from_map`. |
| fn convert_value(v: Value) -> Result<Option<Box<fsys::Value>>, Error> { |
| Ok(match v { |
| Value::Null => None, |
| Value::Bool(b) => Some(Box::new(fsys::Value::Bit(b))), |
| Value::Number(n) => { |
| if let Some(i) = n.as_i64() { |
| Some(Box::new(fsys::Value::Inum(i))) |
| } else if let Some(f) = n.as_f64() { |
| Some(Box::new(fsys::Value::Fnum(f))) |
| } else { |
| return Err(Error::validate(format!("Number is out of range: {}", n))); |
| } |
| } |
| Value::String(s) => Some(Box::new(fsys::Value::Str(s.clone()))), |
| Value::Array(a) => { |
| let vector = fsys::Vector { |
| values: a.into_iter().map(convert_value).collect::<Result<Vec<_>, Error>>()?, |
| }; |
| Some(Box::new(fsys::Value::Vec(vector))) |
| } |
| Value::Object(o) => { |
| let obj = fsys_object_from_map(o)?; |
| Some(Box::new(fsys::Value::Obj(obj))) |
| } |
| }) |
| } |
| |
| // Converts a Map<String, serde_json::Value> to a fuchsia Dictionary. |
| fn dictionary_from_map(in_obj: Map<String, Value>) -> Result<fdata::Dictionary, Error> { |
| let mut entries = vec![]; |
| for (key, v) in in_obj { |
| let value = value_to_dictionary_value(v)?; |
| entries.push(fdata::DictionaryEntry { key, value }); |
| } |
| Ok(fdata::Dictionary { entries: Some(entries), ..fdata::Dictionary::EMPTY }) |
| } |
| |
| // Converts a serde_json::Value into a fuchsia DictionaryValue. Used by `dictionary_from_map`. |
| fn value_to_dictionary_value(value: Value) -> Result<Option<Box<fdata::DictionaryValue>>, Error> { |
| match value { |
| Value::Null => Ok(None), |
| Value::String(s) => Ok(Some(Box::new(fdata::DictionaryValue::Str(s.clone())))), |
| Value::Array(arr) => { |
| let mut strs = vec![]; |
| for val in arr { |
| match val { |
| Value::String(s) => strs.push(s), |
| _ => return Err(Error::validate("Value must be string")), |
| }; |
| } |
| Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs)))) |
| } |
| _ => return Err(Error::validate("Value must be string or list of strings")), |
| } |
| } |
| |
| // 'program' rules denote a set of dictionary entries that may specify lifestyle events with a "lifecycle" prefix key. |
| // All other entries are copied as is. |
| pub fn translate_program(program: &Map<String, Value>) -> Result<fdata::Dictionary, Error> { |
| let mut entries = Vec::new(); |
| for (k, v) in program { |
| match (&k[..], v) { |
| ("lifecycle", Value::Object(events)) => { |
| for (event, subscription) in events { |
| entries.push(fdata::DictionaryEntry { |
| key: format!("lifecycle.{}", event), |
| value: value_to_dictionary_value(subscription.clone())?, |
| }); |
| } |
| } |
| ("lifecycle", _) => { |
| return Err(Error::validate(format!( |
| "Unexpected entry in lifecycle section: {}", |
| v |
| ))); |
| } |
| _ => { |
| entries.push(fdata::DictionaryEntry { |
| key: k.clone(), |
| value: value_to_dictionary_value(v.clone())?, |
| }); |
| } |
| } |
| } |
| |
| Ok(fdata::Dictionary { entries: Some(entries), ..fdata::Dictionary::EMPTY }) |
| } |
| |
| /// `use` rules consume a single capability from one source (parent|framework). |
| fn translate_use(use_in: &Vec<cml::Use>) -> Result<Vec<fsys::UseDecl>, Error> { |
| let mut out_uses = vec![]; |
| for use_ in use_in { |
| if let Some(n) = use_.service() { |
| let source = extract_use_source(use_)?; |
| let target_path = one_target_use_path(use_, use_)?; |
| out_uses.push(fsys::UseDecl::Service(fsys::UseServiceDecl { |
| source: Some(source), |
| source_name: Some(n.clone().into()), |
| target_path: Some(target_path.into()), |
| ..fsys::UseServiceDecl::EMPTY |
| })); |
| } else if let Some(n) = use_.protocol() { |
| let source = extract_use_source(use_)?; |
| let target_paths = |
| all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?; |
| let source_names = n.to_vec(); |
| for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter()) |
| { |
| out_uses.push(fsys::UseDecl::Protocol(fsys::UseProtocolDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(source_name.clone().into()), |
| target_path: Some(target_path.into()), |
| ..fsys::UseProtocolDecl::EMPTY |
| })); |
| } |
| } else if let Some(n) = use_.directory() { |
| let source = extract_use_source(use_)?; |
| let target_path = one_target_use_path(use_, use_)?; |
| let rights = translate::extract_required_rights(use_, "use")?; |
| let subdir = extract_use_subdir(use_); |
| out_uses.push(fsys::UseDecl::Directory(fsys::UseDirectoryDecl { |
| source: Some(source), |
| source_name: Some(n.clone().into()), |
| target_path: Some(target_path.into()), |
| rights: Some(rights), |
| subdir: subdir.map(|s| s.into()), |
| ..fsys::UseDirectoryDecl::EMPTY |
| })); |
| } else if let Some(s) = use_.storage() { |
| let target_path = one_target_use_path(use_, use_)?; |
| out_uses.push(fsys::UseDecl::Storage(fsys::UseStorageDecl { |
| source_name: Some(s.to_string()), |
| target_path: Some(target_path.into()), |
| ..fsys::UseStorageDecl::EMPTY |
| })); |
| } else if let Some(n) = use_.runner() { |
| out_uses.push(fsys::UseDecl::Runner(fsys::UseRunnerDecl { |
| source_name: Some(n.clone().into()), |
| ..fsys::UseRunnerDecl::EMPTY |
| })) |
| } else if let Some(n) = use_.event() { |
| let source = extract_use_event_source(use_)?; |
| let target_names = all_target_capability_names(use_, use_) |
| .ok_or_else(|| Error::internal("no capability"))?; |
| let source_names = n.to_vec(); |
| for target_name in target_names { |
| // When multiple source names are provided, there is no way to alias each one, so |
| // source_name == target_name, |
| // When one source name is provided, source_name may be aliased to a different |
| // target_name, so we use source_names[0] to derive the source_name. |
| let source_name = if source_names.len() == 1 { |
| source_names[0].clone() |
| } else { |
| target_name.clone() |
| }; |
| out_uses.push(fsys::UseDecl::Event(fsys::UseEventDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(source_name.into()), |
| target_name: Some(target_name.into()), |
| // We have already validated that none will be present if we were using many |
| // events. |
| filter: match use_.filter.clone() { |
| Some(dict) => Some(dictionary_from_map(dict)?), |
| None => None, |
| }, |
| ..fsys::UseEventDecl::EMPTY |
| })); |
| } |
| } else if let Some(p) = use_.event_stream() { |
| let target_path = one_target_use_path(use_, use_)?; |
| out_uses.push(fsys::UseDecl::EventStream(fsys::UseEventStreamDecl { |
| target_path: Some(target_path.into()), |
| events: Some(p.to_vec().into_iter().map(|name| name.clone().into()).collect()), |
| ..fsys::UseEventStreamDecl::EMPTY |
| })); |
| } else { |
| return Err(Error::internal(format!("no capability in use declaration"))); |
| }; |
| } |
| Ok(out_uses) |
| } |
| |
| /// `expose` rules route a single capability from one or more sources (self|framework|#<child>) to |
| /// one or more targets (parent|framework). |
| fn translate_expose( |
| expose_in: &Vec<cml::Expose>, |
| all_capability_names: &HashSet<cml::Name>, |
| ) -> Result<Vec<fsys::ExposeDecl>, Error> { |
| let mut out_exposes = vec![]; |
| for expose in expose_in.iter() { |
| let target = extract_expose_target(expose)?; |
| if let Some(n) = expose.service() { |
| let sources = extract_all_expose_sources(expose)?; |
| let target_name = one_target_capability_name(expose, expose)?; |
| for source in sources { |
| out_exposes.push(fsys::ExposeDecl::Service(fsys::ExposeServiceDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(n.clone().into()), |
| target_name: Some(target_name.clone().into()), |
| target: Some(clone_fsys_ref(&target)?), |
| ..fsys::ExposeServiceDecl::EMPTY |
| })) |
| } |
| } else if let Some(n) = expose.protocol() { |
| let source = extract_single_expose_source(expose, Some(all_capability_names))?; |
| let source_names: Vec<_> = n.to_vec(); |
| let target_names = all_target_capability_names(expose, expose) |
| .ok_or_else(|| Error::internal("no capability"))?; |
| for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter()) |
| { |
| out_exposes.push(fsys::ExposeDecl::Protocol(fsys::ExposeProtocolDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(source_name.clone().into()), |
| target_name: Some(target_name.into()), |
| target: Some(clone_fsys_ref(&target)?), |
| ..fsys::ExposeProtocolDecl::EMPTY |
| })) |
| } |
| } else if let Some(n) = expose.directory() { |
| let source = extract_single_expose_source(expose, None)?; |
| let target_name = one_target_capability_name(expose, expose)?; |
| let rights = extract_expose_rights(expose)?; |
| let subdir = extract_expose_subdir(expose); |
| out_exposes.push(fsys::ExposeDecl::Directory(fsys::ExposeDirectoryDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(n.clone().into()), |
| target_name: Some(target_name.into()), |
| target: Some(clone_fsys_ref(&target)?), |
| rights, |
| subdir: subdir.map(|s| s.into()), |
| ..fsys::ExposeDirectoryDecl::EMPTY |
| })) |
| } else if let Some(n) = expose.runner() { |
| let source = extract_single_expose_source(expose, None)?; |
| let target_name = one_target_capability_name(expose, expose)?; |
| out_exposes.push(fsys::ExposeDecl::Runner(fsys::ExposeRunnerDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(n.clone().into()), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| ..fsys::ExposeRunnerDecl::EMPTY |
| })) |
| } else if let Some(n) = expose.resolver() { |
| let source = extract_single_expose_source(expose, None)?; |
| let target_name = one_target_capability_name(expose, expose)?; |
| out_exposes.push(fsys::ExposeDecl::Resolver(fsys::ExposeResolverDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(n.clone().into()), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| ..fsys::ExposeResolverDecl::EMPTY |
| })) |
| } else { |
| return Err(Error::internal(format!("expose: must specify a known capability"))); |
| } |
| } |
| Ok(out_exposes) |
| } |
| |
| /// `offer` rules route multiple capabilities from multiple sources to multiple targets. |
| fn translate_offer( |
| offer_in: &Vec<cml::Offer>, |
| all_children: &HashSet<&cml::Name>, |
| all_collections: &HashSet<&cml::Name>, |
| all_capability_names: &HashSet<cml::Name>, |
| ) -> Result<Vec<fsys::OfferDecl>, Error> { |
| let mut out_offers = vec![]; |
| for offer in offer_in.iter() { |
| if let Some(n) = offer.service() { |
| let sources = extract_all_offer_sources(offer)?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| for (target, target_name) in targets { |
| for source in &sources { |
| out_offers.push(fsys::OfferDecl::Service(fsys::OfferServiceDecl { |
| source_name: Some(n.clone().into()), |
| source: Some(clone_fsys_ref(&source)?), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.clone().into()), |
| ..fsys::OfferServiceDecl::EMPTY |
| })); |
| } |
| } |
| } else if let Some(n) = offer.protocol() { |
| let source = extract_single_offer_source(offer, Some(all_capability_names))?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| let source_names = n.to_vec(); |
| for (target, target_name) in targets { |
| // When multiple source names are provided, there is no way to alias each one, so |
| // source_name == target_name. |
| // When one source name is provided, source_name may be aliased to a different |
| // target_name, so we source_names[0] to derive the source_name. |
| // |
| // TODO: This logic could be simplified to use iter::zip() if |
| // extract_all_targets_for_each_child returned separate vectors for targets and |
| // target_names instead of the cross product of them. |
| let source_name = if source_names.len() == 1 { |
| source_names[0].clone() |
| } else { |
| target_name.clone() |
| }; |
| out_offers.push(fsys::OfferDecl::Protocol(fsys::OfferProtocolDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(source_name.into()), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| dependency_type: Some( |
| offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(), |
| ), |
| ..fsys::OfferProtocolDecl::EMPTY |
| })); |
| } |
| } else if let Some(n) = offer.directory() { |
| let source = extract_single_offer_source(offer, None)?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| for (target, target_name) in targets { |
| out_offers.push(fsys::OfferDecl::Directory(fsys::OfferDirectoryDecl { |
| source_name: Some(n.clone().into()), |
| source: Some(clone_fsys_ref(&source)?), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| rights: extract_offer_rights(offer)?, |
| subdir: extract_offer_subdir(offer).map(|s| s.into()), |
| dependency_type: Some( |
| offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(), |
| ), |
| ..fsys::OfferDirectoryDecl::EMPTY |
| })); |
| } |
| } else if let Some(s) = offer.storage() { |
| let source = extract_single_offer_storage_source(offer)?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| for (target, target_name) in targets { |
| out_offers.push(fsys::OfferDecl::Storage(fsys::OfferStorageDecl { |
| source_name: Some(s.to_string()), |
| source: Some(clone_fsys_ref(&source)?), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| ..fsys::OfferStorageDecl::EMPTY |
| })); |
| } |
| } else if let Some(n) = offer.runner() { |
| let source = extract_single_offer_source(offer, None)?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| for (target, target_name) in targets { |
| out_offers.push(fsys::OfferDecl::Runner(fsys::OfferRunnerDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(n.clone().into()), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| ..fsys::OfferRunnerDecl::EMPTY |
| })); |
| } |
| } else if let Some(n) = offer.resolver() { |
| let source = extract_single_offer_source(offer, None)?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| for (target, target_name) in targets { |
| out_offers.push(fsys::OfferDecl::Resolver(fsys::OfferResolverDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(n.clone().into()), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.into()), |
| ..fsys::OfferResolverDecl::EMPTY |
| })); |
| } |
| } else if let Some(p) = offer.event() { |
| let source = extract_single_offer_source(offer, None)?; |
| let targets = extract_all_targets_for_each_child(offer, all_children, all_collections)?; |
| let source_names = p.to_vec(); |
| for (target, target_name) in targets { |
| // When multiple source names are provided, there is no way to alias each one, so |
| // source_name == target_name. |
| // When one source name is provided, source_name may be aliased to a different |
| // source_name, so we source_names[0] to derive the source_name. |
| let source_name = if source_names.len() == 1 { |
| source_names[0].clone() |
| } else { |
| target_name.clone() |
| }; |
| out_offers.push(fsys::OfferDecl::Event(fsys::OfferEventDecl { |
| source: Some(clone_fsys_ref(&source)?), |
| source_name: Some(source_name.into()), |
| target: Some(clone_fsys_ref(&target)?), |
| target_name: Some(target_name.clone().into()), |
| // We have already validated that none will be present if we were using many |
| // events. |
| filter: match offer.filter.clone() { |
| Some(dict) => Some(dictionary_from_map(dict)?), |
| None => None, |
| }, |
| ..fsys::OfferEventDecl::EMPTY |
| })); |
| } |
| } else { |
| return Err(Error::internal(format!("no capability"))); |
| } |
| } |
| Ok(out_offers) |
| } |
| |
| fn translate_children(children_in: &Vec<cml::Child>) -> Result<Vec<fsys::ChildDecl>, Error> { |
| let mut out_children = vec![]; |
| for child in children_in.iter() { |
| out_children.push(fsys::ChildDecl { |
| name: Some(child.name.clone().into()), |
| url: Some(child.url.clone().into()), |
| startup: Some(child.startup.clone().into()), |
| environment: extract_environment_ref(child.environment.as_ref()).map(|e| e.into()), |
| ..fsys::ChildDecl::EMPTY |
| }); |
| } |
| Ok(out_children) |
| } |
| |
| fn translate_collections( |
| collections_in: &Vec<cml::Collection>, |
| ) -> Result<Vec<fsys::CollectionDecl>, Error> { |
| let mut out_collections = vec![]; |
| for collection in collections_in.iter() { |
| out_collections.push(fsys::CollectionDecl { |
| name: Some(collection.name.clone().into()), |
| durability: Some(collection.durability.clone().into()), |
| environment: extract_environment_ref(collection.environment.as_ref()).map(|e| e.into()), |
| ..fsys::CollectionDecl::EMPTY |
| }); |
| } |
| Ok(out_collections) |
| } |
| |
| fn translate_environments( |
| envs_in: &Vec<cml::Environment>, |
| ) -> Result<Vec<fsys::EnvironmentDecl>, Error> { |
| envs_in |
| .iter() |
| .map(|env| { |
| Ok(fsys::EnvironmentDecl { |
| name: Some(env.name.clone().into()), |
| extends: match env.extends { |
| Some(cml::EnvironmentExtends::Realm) => Some(fsys::EnvironmentExtends::Realm), |
| Some(cml::EnvironmentExtends::None) => Some(fsys::EnvironmentExtends::None), |
| None => Some(fsys::EnvironmentExtends::None), |
| }, |
| runners: env |
| .runners |
| .as_ref() |
| .map(|runners| { |
| runners |
| .iter() |
| .map(translate_runner_registration) |
| .collect::<Result<Vec<_>, Error>>() |
| }) |
| .transpose()?, |
| resolvers: env |
| .resolvers |
| .as_ref() |
| .map(|resolvers| { |
| resolvers |
| .iter() |
| .map(translate_resolver_registration) |
| .collect::<Result<Vec<_>, Error>>() |
| }) |
| .transpose()?, |
| stop_timeout_ms: env.stop_timeout_ms.map(|s| s.0), |
| ..fsys::EnvironmentDecl::EMPTY |
| }) |
| }) |
| .collect() |
| } |
| |
| fn translate_runner_registration( |
| reg: &cml::RunnerRegistration, |
| ) -> Result<fsys::RunnerRegistration, Error> { |
| Ok(fsys::RunnerRegistration { |
| source_name: Some(reg.runner.clone().into()), |
| source: Some(extract_single_offer_source(reg, None)?), |
| target_name: Some(reg.r#as.as_ref().unwrap_or(®.runner).clone().into()), |
| ..fsys::RunnerRegistration::EMPTY |
| }) |
| } |
| |
| fn translate_resolver_registration( |
| reg: &cml::ResolverRegistration, |
| ) -> Result<fsys::ResolverRegistration, Error> { |
| Ok(fsys::ResolverRegistration { |
| resolver: Some(reg.resolver.clone().into()), |
| source: Some(extract_single_offer_source(reg, None)?), |
| scheme: Some( |
| reg.scheme |
| .as_str() |
| .parse::<cm_types::UrlScheme>() |
| .map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))? |
| .into(), |
| ), |
| ..fsys::ResolverRegistration::EMPTY |
| }) |
| } |
| |
| fn extract_use_source(in_obj: &cml::Use) -> Result<fsys::Ref, Error> { |
| match in_obj.from.as_ref() { |
| Some(cml::UseFromRef::Parent) => Ok(fsys::Ref::Parent(fsys::ParentRef {})), |
| Some(cml::UseFromRef::Framework) => Ok(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| Some(cml::UseFromRef::Named(name)) => { |
| Ok(fsys::Ref::Capability(fsys::CapabilityRef { name: name.clone().into() })) |
| } |
| None => Ok(fsys::Ref::Parent(fsys::ParentRef {})), // Default value. |
| } |
| } |
| |
| // Since fsys::Ref is not cloneable, write our own clone function. |
| fn clone_fsys_ref(fsys_ref: &fsys::Ref) -> Result<fsys::Ref, Error> { |
| match fsys_ref { |
| fsys::Ref::Parent(parent_ref) => Ok(fsys::Ref::Parent(parent_ref.clone())), |
| fsys::Ref::Self_(self_ref) => Ok(fsys::Ref::Self_(self_ref.clone())), |
| fsys::Ref::Child(child_ref) => Ok(fsys::Ref::Child(child_ref.clone())), |
| fsys::Ref::Collection(collection_ref) => Ok(fsys::Ref::Collection(collection_ref.clone())), |
| fsys::Ref::Framework(framework_ref) => Ok(fsys::Ref::Framework(framework_ref.clone())), |
| fsys::Ref::Capability(capability_ref) => Ok(fsys::Ref::Capability(capability_ref.clone())), |
| _ => Err(Error::internal("Unknown fsys::Ref found.")), |
| } |
| } |
| |
| fn extract_use_event_source(in_obj: &cml::Use) -> Result<fsys::Ref, Error> { |
| match in_obj.from.as_ref() { |
| Some(cml::UseFromRef::Parent) => Ok(fsys::Ref::Parent(fsys::ParentRef {})), |
| Some(cml::UseFromRef::Framework) => Ok(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| Some(cml::UseFromRef::Named(name)) => { |
| Ok(fsys::Ref::Capability(fsys::CapabilityRef { name: name.clone().into() })) |
| } |
| None => Err(Error::internal(format!("No source \"from\" provided for \"use\""))), |
| } |
| } |
| |
| fn extract_use_subdir(in_obj: &cml::Use) -> Option<cm::RelativePath> { |
| in_obj.subdir.clone() |
| } |
| |
| fn extract_expose_subdir(in_obj: &cml::Expose) -> Option<cm::RelativePath> { |
| in_obj.subdir.clone() |
| } |
| |
| fn extract_offer_subdir(in_obj: &cml::Offer) -> Option<cm::RelativePath> { |
| in_obj.subdir.clone() |
| } |
| |
| fn extract_expose_rights(in_obj: &cml::Expose) -> Result<Option<fio2::Operations>, Error> { |
| match in_obj.rights.as_ref() { |
| Some(rights_tokens) => { |
| let mut rights = Vec::new(); |
| for token in rights_tokens.0.iter() { |
| rights.append(&mut token.expand()) |
| } |
| if rights.is_empty() { |
| return Err(Error::missing_rights( |
| "Rights provided to expose are not well formed.", |
| )); |
| } |
| let mut seen_rights = HashSet::with_capacity(rights.len()); |
| let mut operations: fio2::Operations = fio2::Operations::empty(); |
| for right in rights.iter() { |
| if seen_rights.contains(&right) { |
| return Err(Error::duplicate_rights( |
| "Rights provided to expose are not well formed.", |
| )); |
| } |
| seen_rights.insert(right); |
| operations |= *right; |
| } |
| |
| Ok(Some(operations)) |
| } |
| // Unlike use rights, expose rights can take a None value |
| None => Ok(None), |
| } |
| } |
| |
| fn expose_source_from_ref( |
| reference: &cml::ExposeFromRef, |
| all_capability_names: Option<&HashSet<cml::Name>>, |
| ) -> Result<fsys::Ref, Error> { |
| match reference { |
| cml::ExposeFromRef::Named(name) => { |
| if all_capability_names.is_some() && all_capability_names.unwrap().contains(&name) { |
| return Ok(fsys::Ref::Capability(fsys::CapabilityRef { |
| name: name.clone().into(), |
| })); |
| } |
| Ok(fsys::Ref::Child(fsys::ChildRef { name: name.clone().into(), collection: None })) |
| } |
| cml::ExposeFromRef::Framework => Ok(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| cml::ExposeFromRef::Self_ => Ok(fsys::Ref::Self_(fsys::SelfRef {})), |
| } |
| } |
| |
| fn extract_single_expose_source( |
| in_obj: &cml::Expose, |
| all_capability_names: Option<&HashSet<cml::Name>>, |
| ) -> Result<fsys::Ref, Error> { |
| match &in_obj.from { |
| OneOrMany::One(reference) => expose_source_from_ref(&reference, all_capability_names), |
| OneOrMany::Many(many) => { |
| return Err(Error::internal(format!( |
| "multiple unexpected \"from\" clauses for \"expose\": {:?}", |
| many |
| ))) |
| } |
| } |
| } |
| |
| fn extract_all_expose_sources(in_obj: &cml::Expose) -> Result<Vec<fsys::Ref>, Error> { |
| in_obj.from.to_vec().into_iter().map(|e| expose_source_from_ref(e, None)).collect() |
| } |
| |
| fn extract_offer_rights(in_obj: &cml::Offer) -> Result<Option<fio2::Operations>, Error> { |
| match in_obj.rights.as_ref() { |
| Some(rights_tokens) => { |
| let mut rights = Vec::new(); |
| for token in rights_tokens.0.iter() { |
| rights.append(&mut token.expand()) |
| } |
| if rights.is_empty() { |
| return Err(Error::missing_rights("Rights provided to offer are not well formed.")); |
| } |
| let mut seen_rights = HashSet::with_capacity(rights.len()); |
| let mut operations: fio2::Operations = fio2::Operations::empty(); |
| for right in rights.iter() { |
| if seen_rights.contains(&right) { |
| return Err(Error::duplicate_rights( |
| "Rights provided to offer are not well formed.", |
| )); |
| } |
| seen_rights.insert(right); |
| operations |= *right; |
| } |
| |
| Ok(Some(operations)) |
| } |
| // Unlike use rights, offer rights can take a None value |
| None => Ok(None), |
| } |
| } |
| |
| fn extract_single_offer_source<T>( |
| in_obj: &T, |
| all_capability_names: Option<&HashSet<cml::Name>>, |
| ) -> Result<fsys::Ref, Error> |
| where |
| T: cml::FromClause, |
| { |
| match in_obj.from_() { |
| OneOrMany::One(reference) => { |
| translate::offer_source_from_ref(reference, all_capability_names) |
| } |
| many => { |
| return Err(Error::internal(format!( |
| "multiple unexpected \"from\" clauses for \"offer\": {}", |
| many |
| ))) |
| } |
| } |
| } |
| |
| fn extract_all_offer_sources(in_obj: &cml::Offer) -> Result<Vec<fsys::Ref>, Error> { |
| in_obj |
| .from |
| .to_vec() |
| .into_iter() |
| .map(|r| translate::offer_source_from_ref(r.into(), None)) |
| .collect() |
| } |
| |
| fn extract_single_offer_storage_source(in_obj: &cml::Offer) -> Result<fsys::Ref, Error> { |
| let reference = match &in_obj.from { |
| OneOrMany::One(r) => r, |
| OneOrMany::Many(_) => { |
| return Err(Error::internal(format!( |
| "multiple unexpected \"from\" clauses for \"offer\": {:?}", |
| in_obj.from |
| ))); |
| } |
| }; |
| match reference { |
| cml::OfferFromRef::Parent => Ok(fsys::Ref::Parent(fsys::ParentRef {})), |
| cml::OfferFromRef::Self_ => Ok(fsys::Ref::Self_(fsys::SelfRef {})), |
| other => Err(Error::internal(format!("invalid \"from\" for \"offer\": {:?}", other))), |
| } |
| } |
| |
| fn translate_child_or_collection_ref( |
| reference: cml::AnyRef, |
| all_children: &HashSet<&cml::Name>, |
| all_collections: &HashSet<&cml::Name>, |
| ) -> Result<fsys::Ref, Error> { |
| match reference { |
| cml::AnyRef::Named(name) if all_children.contains(name) => { |
| Ok(fsys::Ref::Child(fsys::ChildRef { name: name.clone().into(), collection: None })) |
| } |
| cml::AnyRef::Named(name) if all_collections.contains(name) => { |
| Ok(fsys::Ref::Collection(fsys::CollectionRef { name: name.clone().into() })) |
| } |
| cml::AnyRef::Named(_) => { |
| Err(Error::internal(format!("dangling reference: \"{}\"", reference))) |
| } |
| _ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))), |
| } |
| } |
| |
| // Return a list of (child, target capability id) expressed in the `offer`. |
| fn extract_all_targets_for_each_child( |
| in_obj: &cml::Offer, |
| all_children: &HashSet<&cml::Name>, |
| all_collections: &HashSet<&cml::Name>, |
| ) -> Result<Vec<(fsys::Ref, cml::Name)>, Error> { |
| let mut out_targets = vec![]; |
| |
| let target_names = all_target_capability_names(in_obj, in_obj) |
| .ok_or_else(|| Error::internal("no capability".to_string()))?; |
| |
| // Validate the "to" references. |
| for to in &in_obj.to.0 { |
| for target_name in &target_names { |
| let target = |
| translate_child_or_collection_ref(to.into(), all_children, all_collections)?; |
| out_targets.push((target, target_name.clone())) |
| } |
| } |
| Ok(out_targets) |
| } |
| |
| /// Return the target paths specified in the given use declaration. |
| fn all_target_use_paths<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<cml::Path>> |
| where |
| T: cml::CapabilityClause, |
| U: cml::AsClause + cml::PathClause, |
| { |
| if let Some(n) = in_obj.service() { |
| if let Some(path) = to_obj.path() { |
| Some(OneOrMany::One(path.clone())) |
| } else { |
| Some(OneOrMany::One(format!("/svc/{}", n).parse().unwrap())) |
| } |
| } else if let Some(p) = in_obj.protocol() { |
| Some(match p { |
| OneOrMany::One(n) => { |
| if let Some(path) = to_obj.path() { |
| OneOrMany::One(path.clone()) |
| } else { |
| OneOrMany::One(format!("/svc/{}", n).parse().unwrap()) |
| } |
| } |
| OneOrMany::Many(v) => { |
| let many = v.iter().map(|n| format!("/svc/{}", n).parse().unwrap()).collect(); |
| OneOrMany::Many(many) |
| } |
| }) |
| } else if let Some(_) = in_obj.directory() { |
| let path = to_obj.path().expect("no path on use directory"); |
| Some(OneOrMany::One(path.clone())) |
| } else if let Some(_) = in_obj.storage() { |
| let path = to_obj.path().expect("no path on use storage"); |
| Some(OneOrMany::One(path.clone())) |
| } else if let Some(_) = in_obj.event_stream() { |
| let path = to_obj.path().expect("no path on event stream"); |
| Some(OneOrMany::One(path.clone())) |
| } else { |
| None |
| } |
| } |
| |
| /// Return the single target path specified in the given use declaration. |
| fn one_target_use_path<T, U>(in_obj: &T, to_obj: &U) -> Result<cml::Path, Error> |
| where |
| T: cml::CapabilityClause, |
| U: cml::AsClause + cml::PathClause, |
| { |
| match all_target_use_paths(in_obj, to_obj) { |
| Some(OneOrMany::One(target_name)) => Ok(target_name), |
| Some(OneOrMany::Many(_)) => { |
| Err(Error::internal("expecting one capability, but multiple provided")) |
| } |
| _ => Err(Error::internal("expecting one capability, but none provided")), |
| } |
| } |
| |
| /// Return the target names or paths specified in the given capability. |
| fn all_target_capability_names<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<cml::Name>> |
| where |
| T: cml::CapabilityClause, |
| U: cml::AsClause + cml::PathClause, |
| { |
| if let Some(as_) = to_obj.r#as() { |
| // We've already validated that when `as` is specified, only 1 source id exists. |
| Some(OneOrMany::One(as_.clone())) |
| } else { |
| if let Some(n) = in_obj.service() { |
| Some(OneOrMany::One(n.clone())) |
| } else if let Some(p) = in_obj.protocol() { |
| Some(p.clone()) |
| } else if let Some(d) = in_obj.directory() { |
| Some(OneOrMany::One(d.clone())) |
| } else if let Some(n) = in_obj.storage() { |
| Some(OneOrMany::One(n.clone())) |
| } else if let Some(n) = in_obj.runner() { |
| Some(OneOrMany::One(n.clone())) |
| } else if let Some(n) = in_obj.resolver() { |
| Some(OneOrMany::One(n.clone())) |
| } else if let Some(e) = in_obj.event() { |
| Some(e.clone()) |
| } else { |
| None |
| } |
| } |
| } |
| |
| /// Return the single target name specified in the given routing declaration. |
| fn one_target_capability_name<T, U>(in_obj: &T, to_obj: &U) -> Result<cml::Name, Error> |
| where |
| T: cml::CapabilityClause, |
| U: cml::AsClause + cml::PathClause, |
| { |
| match all_target_capability_names(in_obj, to_obj) { |
| Some(OneOrMany::One(target_name)) => Ok(target_name), |
| Some(OneOrMany::Many(_)) => { |
| Err(Error::internal("expecting one capability, but multiple provided")) |
| } |
| _ => Err(Error::internal("expecting one capability, but none provided")), |
| } |
| } |
| |
| fn extract_expose_target(in_obj: &cml::Expose) -> Result<fsys::Ref, Error> { |
| match &in_obj.to { |
| Some(cml::ExposeToRef::Parent) => Ok(fsys::Ref::Parent(fsys::ParentRef {})), |
| Some(cml::ExposeToRef::Framework) => Ok(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| None => Ok(fsys::Ref::Parent(fsys::ParentRef {})), |
| } |
| } |
| |
| fn extract_environment_ref(r: Option<&cml::EnvironmentRef>) -> Option<cm::Name> { |
| r.map(|r| { |
| let cml::EnvironmentRef::Named(name) = r; |
| name.clone() |
| }) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use fidl::encoding::decode_persistent; |
| use matches::assert_matches; |
| use serde_json::json; |
| use std::fs::File; |
| use std::io::{self, Read, Write}; |
| use tempfile::TempDir; |
| |
| macro_rules! test_compile { |
| ( |
| $( |
| $(#[$m:meta])* |
| $test_name:ident => { |
| input = $input:expr, |
| output = $result:expr, |
| }, |
| )+ |
| ) => { |
| $( |
| $(#[$m])* |
| #[test] |
| fn $test_name() { |
| let tmp_dir = TempDir::new().unwrap(); |
| let tmp_in_path = tmp_dir.path().join("test.cml"); |
| let tmp_out_path = tmp_dir.path().join("test.cm"); |
| compile_test(tmp_in_path, tmp_out_path, None, $input, $result).expect("compilation failed"); |
| } |
| )+ |
| } |
| } |
| |
| fn compile_test( |
| in_path: PathBuf, |
| out_path: PathBuf, |
| include_path: Option<PathBuf>, |
| input: serde_json::value::Value, |
| expected_output: fsys::ComponentDecl, |
| ) -> Result<(), Error> { |
| File::create(&in_path).unwrap().write_all(format!("{}", input).as_bytes()).unwrap(); |
| compile(&in_path, &out_path.clone(), None, include_path.unwrap_or(PathBuf::new()))?; |
| let mut buffer = Vec::new(); |
| fs::File::open(&out_path).unwrap().read_to_end(&mut buffer).unwrap(); |
| let output: fsys::ComponentDecl = decode_persistent(&buffer).unwrap(); |
| assert_eq!(output, expected_output); |
| Ok(()) |
| } |
| |
| fn default_component_decl() -> fsys::ComponentDecl { |
| fsys::ComponentDecl::EMPTY |
| } |
| |
| test_compile! { |
| test_compile_empty => { |
| input = json!({}), |
| output = default_component_decl(), |
| }, |
| |
| test_compile_empty_includes => { |
| input = json!({ "include": [] }), |
| output = default_component_decl(), |
| }, |
| |
| test_compile_program => { |
| input = json!({ |
| "program": { |
| "binary": "bin/app" |
| }, |
| "use": [ |
| { "runner": "elf" } |
| ] |
| }), |
| output = fsys::ComponentDecl { |
| program: Some(fdata::Dictionary { |
| entries: Some(vec![fdata::DictionaryEntry { |
| key: "binary".to_string(), |
| value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))), |
| }]), |
| ..fdata::Dictionary::EMPTY |
| }), |
| uses: Some(vec![fsys::UseDecl::Runner ( |
| fsys::UseRunnerDecl { |
| source_name: Some("elf".to_string()), |
| ..fsys::UseRunnerDecl::EMPTY |
| } |
| )]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_program_with_lifecycle => { |
| input = json!({ |
| "program": { |
| "binary": "bin/app", |
| "lifecycle": { |
| "stop_event": "notify", |
| } |
| }, |
| "use": [ |
| { "runner": "elf" } |
| ] |
| }), |
| output = fsys::ComponentDecl { |
| program: Some(fdata::Dictionary { |
| entries: Some(vec![ |
| fdata::DictionaryEntry { |
| key: "binary".to_string(), |
| value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))), |
| }, |
| fdata::DictionaryEntry { |
| key: "lifecycle.stop_event".to_string(), |
| value: Some(Box::new(fdata::DictionaryValue::Str("notify".to_string()))), |
| }, |
| ]), |
| ..fdata::Dictionary::EMPTY |
| }), |
| uses: Some(vec![fsys::UseDecl::Runner ( |
| fsys::UseRunnerDecl { |
| source_name: Some("elf".to_string()), |
| ..fsys::UseRunnerDecl::EMPTY |
| } |
| )]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_use => { |
| input = json!({ |
| "use": [ |
| { "service": "CoolFonts", "path": "/svc/fuchsia.fonts.Provider" }, |
| { "service": "fuchsia.sys2.Realm", "from": "framework" }, |
| { "protocol": "LegacyCoolFonts", "path": "/svc/fuchsia.fonts.LegacyProvider" }, |
| { "protocol": "fuchsia.sys2.LegacyRealm", "from": "framework" }, |
| { "protocol": "fuchsia.sys2.StorageAdmin", "from": "#data-storage" }, |
| { "directory": "assets", "rights" : ["read_bytes"], "path": "/data/assets" }, |
| { |
| "directory": "config", |
| "path": "/data/config", |
| "from": "parent", |
| "rights": ["read_bytes"], |
| "subdir": "fonts", |
| }, |
| { "storage": "hippos", "path": "/hippos" }, |
| { "storage": "cache", "path": "/tmp" }, |
| { "runner": "elf" }, |
| { "runner": "web" }, |
| { "event": "destroyed", "from": "parent" }, |
| { "event": ["started", "stopped"], "from": "framework" }, |
| { |
| "event": "capability_ready", |
| "as": "diagnostics", |
| "from": "parent", |
| "filter": { "name": "diagnostics" } |
| }, |
| ], |
| "capabilities": [ |
| { |
| "storage": "data-storage", |
| "from": "parent", |
| "backing_dir": "minfs" |
| } |
| ] |
| }), |
| output = fsys::ComponentDecl { |
| uses: Some(vec![ |
| fsys::UseDecl::Service ( |
| fsys::UseServiceDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("CoolFonts".to_string()), |
| target_path: Some("/svc/fuchsia.fonts.Provider".to_string()), |
| ..fsys::UseServiceDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Service ( |
| fsys::UseServiceDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("fuchsia.sys2.Realm".to_string()), |
| target_path: Some("/svc/fuchsia.sys2.Realm".to_string()), |
| ..fsys::UseServiceDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Protocol ( |
| fsys::UseProtocolDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("LegacyCoolFonts".to_string()), |
| target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()), |
| ..fsys::UseProtocolDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Protocol ( |
| fsys::UseProtocolDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("fuchsia.sys2.LegacyRealm".to_string()), |
| target_path: Some("/svc/fuchsia.sys2.LegacyRealm".to_string()), |
| ..fsys::UseProtocolDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Protocol ( |
| fsys::UseProtocolDecl { |
| source: Some(fsys::Ref::Capability(fsys::CapabilityRef { name: "data-storage".to_string() })), |
| source_name: Some("fuchsia.sys2.StorageAdmin".to_string()), |
| target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()), |
| ..fsys::UseProtocolDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Directory ( |
| fsys::UseDirectoryDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("assets".to_string()), |
| target_path: Some("/data/assets".to_string()), |
| rights: Some(fio2::Operations::ReadBytes), |
| subdir: None, |
| ..fsys::UseDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Directory ( |
| fsys::UseDirectoryDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("config".to_string()), |
| target_path: Some("/data/config".to_string()), |
| rights: Some(fio2::Operations::ReadBytes), |
| subdir: Some("fonts".to_string()), |
| ..fsys::UseDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Storage ( |
| fsys::UseStorageDecl { |
| source_name: Some("hippos".to_string()), |
| target_path: Some("/hippos".to_string()), |
| ..fsys::UseStorageDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Storage ( |
| fsys::UseStorageDecl { |
| source_name: Some("cache".to_string()), |
| target_path: Some("/tmp".to_string()), |
| ..fsys::UseStorageDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Runner ( |
| fsys::UseRunnerDecl { |
| source_name: Some("elf".to_string()), |
| ..fsys::UseRunnerDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Runner ( |
| fsys::UseRunnerDecl { |
| source_name: Some("web".to_string()), |
| ..fsys::UseRunnerDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Event ( |
| fsys::UseEventDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("destroyed".to_string()), |
| target_name: Some("destroyed".to_string()), |
| filter: None, |
| ..fsys::UseEventDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Event ( |
| fsys::UseEventDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("started".to_string()), |
| target_name: Some("started".to_string()), |
| filter: None, |
| ..fsys::UseEventDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Event ( |
| fsys::UseEventDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("stopped".to_string()), |
| target_name: Some("stopped".to_string()), |
| filter: None, |
| ..fsys::UseEventDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Event ( |
| fsys::UseEventDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("capability_ready".to_string()), |
| target_name: Some("diagnostics".to_string()), |
| filter: Some(fdata::Dictionary { |
| entries: Some(vec![ |
| fdata::DictionaryEntry { |
| key: "name".to_string(), |
| value: Some(Box::new(fdata::DictionaryValue::Str("diagnostics".to_string()))), |
| }, |
| ]), |
| ..fdata::Dictionary::EMPTY |
| }), |
| ..fsys::UseEventDecl::EMPTY |
| } |
| ), |
| ]), |
| capabilities: Some(vec![ |
| fsys::CapabilityDecl::Storage(fsys::StorageDecl { |
| name: Some("data-storage".to_string()), |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| backing_dir: Some("minfs".to_string()), |
| subdir: None, |
| ..fsys::StorageDecl::EMPTY |
| }), |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_expose => { |
| input = json!({ |
| "expose": [ |
| { |
| "service": "fuchsia.logger.Log", |
| "from": "#logger", |
| "as": "fuchsia.logger.Log2", |
| }, |
| { |
| "service": "my.service.Service", |
| "from": ["#logger", "self"], |
| }, |
| { |
| "protocol": "fuchsia.logger.Log", |
| "from": "#logger", |
| "as": "fuchsia.logger.LegacyLog", |
| "to": "parent" |
| }, |
| { |
| "protocol": [ "A", "B" ], |
| "from": "self", |
| "to": "parent" |
| }, |
| { |
| "protocol": "C", |
| "from": "#data-storage", |
| }, |
| { |
| "directory": "blob", |
| "from": "self", |
| "to": "framework", |
| "rights": ["r*"], |
| }, |
| { "directory": "hub", "from": "framework" }, |
| { "runner": "web", "from": "self" }, |
| { "runner": "web", "from": "#logger", "to": "parent", "as": "web-rename" }, |
| { "resolver": "my_resolver", "from": "#logger", "to": "parent", "as": "pkg_resolver" } |
| ], |
| "capabilities": [ |
| { "service": "my.service.Service" }, |
| { "protocol": "A" }, |
| { "protocol": "B" }, |
| { |
| "directory": "blob", |
| "path": "/volumes/blobfs/blob", |
| "rights": ["r*"], |
| }, |
| { |
| "runner": "web", |
| "path": "/svc/fuchsia.component.ComponentRunner", |
| "from": "self", |
| }, |
| { |
| "storage": "data-storage", |
| "from": "parent", |
| "backing_dir": "minfs" |
| }, |
| ], |
| "children": [ |
| { |
| "name": "logger", |
| "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm" |
| }, |
| ] |
| }), |
| output = fsys::ComponentDecl { |
| exposes: Some(vec![ |
| fsys::ExposeDecl::Service ( |
| fsys::ExposeServiceDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("fuchsia.logger.Log".to_string()), |
| target_name: Some("fuchsia.logger.Log2".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| ..fsys::ExposeServiceDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Service ( |
| fsys::ExposeServiceDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("my.service.Service".to_string()), |
| target_name: Some("my.service.Service".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| ..fsys::ExposeServiceDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Service ( |
| fsys::ExposeServiceDecl { |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_name: Some("my.service.Service".to_string()), |
| target_name: Some("my.service.Service".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| ..fsys::ExposeServiceDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Protocol ( |
| fsys::ExposeProtocolDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("fuchsia.logger.Log".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("fuchsia.logger.LegacyLog".to_string()), |
| ..fsys::ExposeProtocolDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Protocol ( |
| fsys::ExposeProtocolDecl { |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_name: Some("A".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("A".to_string()), |
| ..fsys::ExposeProtocolDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Protocol ( |
| fsys::ExposeProtocolDecl { |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_name: Some("B".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("B".to_string()), |
| ..fsys::ExposeProtocolDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Protocol ( |
| fsys::ExposeProtocolDecl { |
| source: Some(fsys::Ref::Capability(fsys::CapabilityRef { |
| name: "data-storage".to_string(), |
| })), |
| source_name: Some("C".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("C".to_string()), |
| ..fsys::ExposeProtocolDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Directory ( |
| fsys::ExposeDirectoryDecl { |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_name: Some("blob".to_string()), |
| target: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| target_name: Some("blob".to_string()), |
| rights: Some( |
| fio2::Operations::Connect | fio2::Operations::Enumerate | |
| fio2::Operations::Traverse | fio2::Operations::ReadBytes | |
| fio2::Operations::GetAttributes |
| ), |
| subdir: None, |
| ..fsys::ExposeDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Directory ( |
| fsys::ExposeDirectoryDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("hub".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("hub".to_string()), |
| rights: None, |
| subdir: None, |
| ..fsys::ExposeDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Runner ( |
| fsys::ExposeRunnerDecl { |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_name: Some("web".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("web".to_string()), |
| ..fsys::ExposeRunnerDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Runner ( |
| fsys::ExposeRunnerDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("web".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("web-rename".to_string()), |
| ..fsys::ExposeRunnerDecl::EMPTY |
| } |
| ), |
| fsys::ExposeDecl::Resolver ( |
| fsys::ExposeResolverDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("my_resolver".to_string()), |
| target: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("pkg_resolver".to_string()), |
| ..fsys::ExposeResolverDecl::EMPTY |
| } |
| ), |
| ]), |
| offers: None, |
| capabilities: Some(vec![ |
| fsys::CapabilityDecl::Service ( |
| fsys::ServiceDecl { |
| name: Some("my.service.Service".to_string()), |
| source_path: Some("/svc/my.service.Service".to_string()), |
| ..fsys::ServiceDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Protocol ( |
| fsys::ProtocolDecl { |
| name: Some("A".to_string()), |
| source_path: Some("/svc/A".to_string()), |
| ..fsys::ProtocolDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Protocol ( |
| fsys::ProtocolDecl { |
| name: Some("B".to_string()), |
| source_path: Some("/svc/B".to_string()), |
| ..fsys::ProtocolDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Directory ( |
| fsys::DirectoryDecl { |
| name: Some("blob".to_string()), |
| source_path: Some("/volumes/blobfs/blob".to_string()), |
| rights: Some(fio2::Operations::Connect | fio2::Operations::Enumerate | |
| fio2::Operations::Traverse | fio2::Operations::ReadBytes | |
| fio2::Operations::GetAttributes |
| ), |
| ..fsys::DirectoryDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Runner ( |
| fsys::RunnerDecl { |
| name: Some("web".to_string()), |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_path: Some("/svc/fuchsia.component.ComponentRunner".to_string()), |
| ..fsys::RunnerDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Storage(fsys::StorageDecl { |
| name: Some("data-storage".to_string()), |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| backing_dir: Some("minfs".to_string()), |
| subdir: None, |
| ..fsys::StorageDecl::EMPTY |
| }), |
| ]), |
| children: Some(vec![ |
| fsys::ChildDecl { |
| name: Some("logger".to_string()), |
| url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()), |
| startup: Some(fsys::StartupMode::Lazy), |
| environment: None, |
| ..fsys::ChildDecl::EMPTY |
| } |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_offer => { |
| input = json!({ |
| "offer": [ |
| { |
| "service": "fuchsia.logger.Log", |
| "from": "#logger", |
| "to": [ "#netstack" ] |
| }, |
| { |
| "service": "fuchsia.logger.Log", |
| "from": "#logger", |
| "to": [ "#modular" ], |
| "as": "fuchsia.logger.Log2", |
| }, |
| { |
| "service": "my.service.Service", |
| "from": ["#logger", "self"], |
| "to": [ "#netstack" ] |
| }, |
| { |
| "protocol": "fuchsia.logger.LegacyLog", |
| "from": "#logger", |
| "to": [ "#netstack" ], |
| "dependency": "weak_for_migration" |
| }, |
| { |
| "protocol": "fuchsia.logger.LegacyLog", |
| "from": "#logger", |
| "to": [ "#modular" ], |
| "as": "fuchsia.logger.LegacySysLog", |
| "dependency": "strong" |
| }, |
| { |
| "protocol": [ |
| "fuchsia.setui.SetUiService", |
| "fuchsia.test.service.Name" |
| ], |
| "from": "parent", |
| "to": [ "#modular" ] |
| }, |
| { |
| "protocol": "fuchsia.sys2.StorageAdmin", |
| "from": "#data", |
| "to": [ "#modular" ], |
| }, |
| { |
| "directory": "assets", |
| "from": "parent", |
| "to": [ "#netstack" ], |
| "dependency": "weak_for_migration" |
| }, |
| { |
| "directory": "data", |
| "from": "parent", |
| "to": [ "#modular" ], |
| "as": "assets", |
| "subdir": "index/file", |
| "dependency": "strong" |
| }, |
| { |
| "directory": "hub", |
| "from": "framework", |
| "to": [ "#modular" ], |
| "as": "hub", |
| }, |
| { |
| "storage": "data", |
| "from": "self", |
| "to": [ |
| "#netstack", |
| "#modular" |
| ], |
| }, |
| { |
| "runner": "web", |
| "from": "parent", |
| "to": [ "#modular" ], |
| }, |
| { |
| "runner": "elf", |
| "from": "parent", |
| "to": [ "#modular" ], |
| "as": "elf-renamed", |
| }, |
| { |
| "event": "destroyed", |
| "from": "framework", |
| "to": [ "#netstack"], |
| "as": "destroyed_net" |
| }, |
| { |
| "event": [ "stopped", "started" ], |
| "from": "parent", |
| "to": [ "#modular" ], |
| }, |
| { |
| "event": "capability_ready", |
| "from": "parent", |
| "to": [ "#netstack" ], |
| "as": "net-ready", |
| "filter": { |
| "name": [ |
| "diagnostics", |
| "foo" |
| ], |
| } |
| }, |
| { |
| "resolver": "my_resolver", |
| "from": "parent", |
| "to": [ "#modular" ], |
| "as": "pkg_resolver", |
| }, |
| ], |
| "children": [ |
| { |
| "name": "logger", |
| "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm" |
| }, |
| { |
| "name": "netstack", |
| "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm" |
| }, |
| ], |
| "collections": [ |
| { |
| "name": "modular", |
| "durability": "persistent", |
| }, |
| ], |
| "capabilities": [ |
| { "service": "my.service.Service" }, |
| { |
| "storage": "data", |
| "backing_dir": "minfs", |
| "from": "#logger", |
| }, |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| offers: Some(vec![ |
| fsys::OfferDecl::Service ( |
| fsys::OfferServiceDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("fuchsia.logger.Log".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("fuchsia.logger.Log".to_string()), |
| ..fsys::OfferServiceDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Service ( |
| fsys::OfferServiceDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("fuchsia.logger.Log".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("fuchsia.logger.Log2".to_string()), |
| ..fsys::OfferServiceDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Service ( |
| fsys::OfferServiceDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("my.service.Service".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("my.service.Service".to_string()), |
| ..fsys::OfferServiceDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Service ( |
| fsys::OfferServiceDecl { |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_name: Some("my.service.Service".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("my.service.Service".to_string()), |
| ..fsys::OfferServiceDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Protocol ( |
| fsys::OfferProtocolDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("fuchsia.logger.LegacyLog".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("fuchsia.logger.LegacyLog".to_string()), |
| dependency_type: Some(fsys::DependencyType::WeakForMigration), |
| ..fsys::OfferProtocolDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Protocol ( |
| fsys::OfferProtocolDecl { |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| source_name: Some("fuchsia.logger.LegacyLog".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("fuchsia.logger.LegacySysLog".to_string()), |
| dependency_type: Some(fsys::DependencyType::Strong), |
| ..fsys::OfferProtocolDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Protocol ( |
| fsys::OfferProtocolDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("fuchsia.setui.SetUiService".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("fuchsia.setui.SetUiService".to_string()), |
| dependency_type: Some(fsys::DependencyType::Strong), |
| ..fsys::OfferProtocolDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Protocol ( |
| fsys::OfferProtocolDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("fuchsia.test.service.Name".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("fuchsia.test.service.Name".to_string()), |
| dependency_type: Some(fsys::DependencyType::Strong), |
| ..fsys::OfferProtocolDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Protocol ( |
| fsys::OfferProtocolDecl { |
| source: Some(fsys::Ref::Capability(fsys::CapabilityRef { |
| name: "data".to_string(), |
| })), |
| source_name: Some("fuchsia.sys2.StorageAdmin".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("fuchsia.sys2.StorageAdmin".to_string()), |
| dependency_type: Some(fsys::DependencyType::Strong), |
| ..fsys::OfferProtocolDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Directory ( |
| fsys::OfferDirectoryDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("assets".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("assets".to_string()), |
| rights: None, |
| subdir: None, |
| dependency_type: Some(fsys::DependencyType::WeakForMigration), |
| ..fsys::OfferDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Directory ( |
| fsys::OfferDirectoryDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("data".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("assets".to_string()), |
| rights: None, |
| subdir: Some("index/file".to_string()), |
| dependency_type: Some(fsys::DependencyType::Strong), |
| ..fsys::OfferDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Directory ( |
| fsys::OfferDirectoryDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("hub".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("hub".to_string()), |
| rights: None, |
| subdir: None, |
| dependency_type: Some(fsys::DependencyType::Strong), |
| ..fsys::OfferDirectoryDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Storage ( |
| fsys::OfferStorageDecl { |
| source_name: Some("data".to_string()), |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("data".to_string()), |
| ..fsys::OfferStorageDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Storage ( |
| fsys::OfferStorageDecl { |
| source_name: Some("data".to_string()), |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("data".to_string()), |
| ..fsys::OfferStorageDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Runner ( |
| fsys::OfferRunnerDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("web".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("web".to_string()), |
| ..fsys::OfferRunnerDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Runner ( |
| fsys::OfferRunnerDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("elf".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("elf-renamed".to_string()), |
| ..fsys::OfferRunnerDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Event ( |
| fsys::OfferEventDecl { |
| source: Some(fsys::Ref::Framework(fsys::FrameworkRef {})), |
| source_name: Some("destroyed".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("destroyed_net".to_string()), |
| filter: None, |
| ..fsys::OfferEventDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Event ( |
| fsys::OfferEventDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("stopped".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("stopped".to_string()), |
| filter: None, |
| ..fsys::OfferEventDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Event ( |
| fsys::OfferEventDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("started".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("started".to_string()), |
| filter: None, |
| ..fsys::OfferEventDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Event ( |
| fsys::OfferEventDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("capability_ready".to_string()), |
| target: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "netstack".to_string(), |
| collection: None, |
| })), |
| target_name: Some("net-ready".to_string()), |
| filter: Some(fdata::Dictionary { |
| entries: Some(vec![ |
| fdata::DictionaryEntry { |
| key: "name".to_string(), |
| value: Some(Box::new(fdata::DictionaryValue::StrVec( |
| vec!["diagnostics".to_string(), "foo".to_string()] |
| ))), |
| }, |
| ]), |
| ..fdata::Dictionary::EMPTY |
| }), |
| ..fsys::OfferEventDecl::EMPTY |
| } |
| ), |
| fsys::OfferDecl::Resolver ( |
| fsys::OfferResolverDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("my_resolver".to_string()), |
| target: Some(fsys::Ref::Collection(fsys::CollectionRef { |
| name: "modular".to_string(), |
| })), |
| target_name: Some("pkg_resolver".to_string()), |
| ..fsys::OfferResolverDecl::EMPTY |
| } |
| ), |
| ]), |
| capabilities: Some(vec![ |
| fsys::CapabilityDecl::Service ( |
| fsys::ServiceDecl { |
| name: Some("my.service.Service".to_string()), |
| source_path: Some("/svc/my.service.Service".to_string()), |
| ..fsys::ServiceDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Storage ( |
| fsys::StorageDecl { |
| name: Some("data".to_string()), |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "logger".to_string(), |
| collection: None, |
| })), |
| backing_dir: Some("minfs".to_string()), |
| subdir: None, |
| ..fsys::StorageDecl::EMPTY |
| } |
| ) |
| ]), |
| children: Some(vec![ |
| fsys::ChildDecl { |
| name: Some("logger".to_string()), |
| url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()), |
| startup: Some(fsys::StartupMode::Lazy), |
| environment: None, |
| ..fsys::ChildDecl::EMPTY |
| }, |
| fsys::ChildDecl { |
| name: Some("netstack".to_string()), |
| url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()), |
| startup: Some(fsys::StartupMode::Lazy), |
| environment: None, |
| ..fsys::ChildDecl::EMPTY |
| }, |
| ]), |
| collections: Some(vec![ |
| fsys::CollectionDecl { |
| name: Some("modular".to_string()), |
| durability: Some(fsys::Durability::Persistent), |
| environment: None, |
| ..fsys::CollectionDecl::EMPTY |
| } |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_children => { |
| input = json!({ |
| "children": [ |
| { |
| "name": "logger", |
| "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm", |
| }, |
| { |
| "name": "gmail", |
| "url": "https://www.google.com/gmail", |
| "startup": "eager", |
| }, |
| { |
| "name": "echo", |
| "url": "fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm", |
| "startup": "lazy", |
| "environment": "#myenv", |
| }, |
| ], |
| "environments": [ |
| { |
| "name": "myenv", |
| "extends": "realm", |
| }, |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| children: Some(vec![ |
| fsys::ChildDecl { |
| name: Some("logger".to_string()), |
| url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()), |
| startup: Some(fsys::StartupMode::Lazy), |
| environment: None, |
| ..fsys::ChildDecl::EMPTY |
| }, |
| fsys::ChildDecl { |
| name: Some("gmail".to_string()), |
| url: Some("https://www.google.com/gmail".to_string()), |
| startup: Some(fsys::StartupMode::Eager), |
| environment: None, |
| ..fsys::ChildDecl::EMPTY |
| }, |
| fsys::ChildDecl { |
| name: Some("echo".to_string()), |
| url: Some("fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm".to_string()), |
| startup: Some(fsys::StartupMode::Lazy), |
| environment: Some("myenv".to_string()), |
| ..fsys::ChildDecl::EMPTY |
| } |
| ]), |
| environments: Some(vec![ |
| fsys::EnvironmentDecl { |
| name: Some("myenv".to_string()), |
| extends: Some(fsys::EnvironmentExtends::Realm), |
| runners: None, |
| resolvers: None, |
| stop_timeout_ms: None, |
| ..fsys::EnvironmentDecl::EMPTY |
| } |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_collections => { |
| input = json!({ |
| "collections": [ |
| { |
| "name": "modular", |
| "durability": "persistent", |
| }, |
| { |
| "name": "tests", |
| "durability": "transient", |
| "environment": "#myenv", |
| }, |
| ], |
| "environments": [ |
| { |
| "name": "myenv", |
| "extends": "realm", |
| } |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| collections: Some(vec![ |
| fsys::CollectionDecl { |
| name: Some("modular".to_string()), |
| durability: Some(fsys::Durability::Persistent), |
| environment: None, |
| ..fsys::CollectionDecl::EMPTY |
| }, |
| fsys::CollectionDecl { |
| name: Some("tests".to_string()), |
| durability: Some(fsys::Durability::Transient), |
| environment: Some("myenv".to_string()), |
| ..fsys::CollectionDecl::EMPTY |
| } |
| ]), |
| environments: Some(vec![ |
| fsys::EnvironmentDecl { |
| name: Some("myenv".to_string()), |
| extends: Some(fsys::EnvironmentExtends::Realm), |
| runners: None, |
| resolvers: None, |
| stop_timeout_ms: None, |
| ..fsys::EnvironmentDecl::EMPTY |
| } |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_capabilities => { |
| input = json!({ |
| "capabilities": [ |
| { |
| "service": "myservice", |
| "path": "/service", |
| }, |
| { |
| "service": "myservice2", |
| }, |
| { |
| "protocol": "myprotocol", |
| "path": "/protocol", |
| }, |
| { |
| "protocol": "myprotocol2", |
| }, |
| { |
| "protocol": [ "myprotocol3", "myprotocol4" ], |
| }, |
| { |
| "directory": "mydirectory", |
| "path": "/directory", |
| "rights": [ "connect" ], |
| }, |
| { |
| "storage": "mystorage", |
| "backing_dir": "storage", |
| "from": "#minfs", |
| }, |
| { |
| "storage": "mystorage2", |
| "backing_dir": "storage2", |
| "from": "#minfs", |
| }, |
| { |
| "runner": "myrunner", |
| "path": "/runner", |
| "from": "self" |
| }, |
| { |
| "resolver": "myresolver", |
| "path": "/resolver" |
| }, |
| ], |
| "children": [ |
| { |
| "name": "minfs", |
| "url": "fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm", |
| }, |
| ] |
| }), |
| output = fsys::ComponentDecl { |
| capabilities: Some(vec![ |
| fsys::CapabilityDecl::Service ( |
| fsys::ServiceDecl { |
| name: Some("myservice".to_string()), |
| source_path: Some("/service".to_string()), |
| ..fsys::ServiceDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Service ( |
| fsys::ServiceDecl { |
| name: Some("myservice2".to_string()), |
| source_path: Some("/svc/myservice2".to_string()), |
| ..fsys::ServiceDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Protocol ( |
| fsys::ProtocolDecl { |
| name: Some("myprotocol".to_string()), |
| source_path: Some("/protocol".to_string()), |
| ..fsys::ProtocolDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Protocol ( |
| fsys::ProtocolDecl { |
| name: Some("myprotocol2".to_string()), |
| source_path: Some("/svc/myprotocol2".to_string()), |
| ..fsys::ProtocolDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Protocol ( |
| fsys::ProtocolDecl { |
| name: Some("myprotocol3".to_string()), |
| source_path: Some("/svc/myprotocol3".to_string()), |
| ..fsys::ProtocolDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Protocol ( |
| fsys::ProtocolDecl { |
| name: Some("myprotocol4".to_string()), |
| source_path: Some("/svc/myprotocol4".to_string()), |
| ..fsys::ProtocolDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Directory ( |
| fsys::DirectoryDecl { |
| name: Some("mydirectory".to_string()), |
| source_path: Some("/directory".to_string()), |
| rights: Some(fio2::Operations::Connect), |
| ..fsys::DirectoryDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Storage ( |
| fsys::StorageDecl { |
| name: Some("mystorage".to_string()), |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "minfs".to_string(), |
| collection: None, |
| })), |
| backing_dir: Some("storage".to_string()), |
| subdir: None, |
| ..fsys::StorageDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Storage ( |
| fsys::StorageDecl { |
| name: Some("mystorage2".to_string()), |
| source: Some(fsys::Ref::Child(fsys::ChildRef { |
| name: "minfs".to_string(), |
| collection: None, |
| })), |
| backing_dir: Some("storage2".to_string()), |
| subdir: None, |
| ..fsys::StorageDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Runner ( |
| fsys::RunnerDecl { |
| name: Some("myrunner".to_string()), |
| source: Some(fsys::Ref::Self_(fsys::SelfRef {})), |
| source_path: Some("/runner".to_string()), |
| ..fsys::RunnerDecl::EMPTY |
| } |
| ), |
| fsys::CapabilityDecl::Resolver ( |
| fsys::ResolverDecl { |
| name: Some("myresolver".to_string()), |
| source_path: Some("/resolver".to_string()), |
| ..fsys::ResolverDecl::EMPTY |
| } |
| ) |
| ]), |
| children: Some(vec![ |
| fsys::ChildDecl { |
| name: Some("minfs".to_string()), |
| url: Some("fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm".to_string()), |
| startup: Some(fsys::StartupMode::Lazy), |
| environment: None, |
| ..fsys::ChildDecl::EMPTY |
| } |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_facets => { |
| input = json!({ |
| "facets": { |
| "metadata": { |
| "title": "foo", |
| "authors": [ "me", "you" ], |
| "year": 2018 |
| } |
| } |
| }), |
| output = fsys::ComponentDecl { |
| facets: Some(fsys::Object { |
| entries: vec![ |
| fsys::Entry { |
| key: "metadata".to_string(), |
| value: Some(Box::new(fsys::Value::Obj(fsys::Object { |
| entries: vec![ |
| fsys::Entry { |
| key: "authors".to_string(), |
| value: Some(Box::new(fsys::Value::Vec ( |
| fsys::Vector { |
| values: vec![ |
| Some(Box::new(fsys::Value::Str("me".to_string()))), |
| Some(Box::new(fsys::Value::Str("you".to_string()))), |
| ] |
| } |
| ))), |
| }, |
| fsys::Entry { |
| key: "title".to_string(), |
| value: Some(Box::new(fsys::Value::Str("foo".to_string()))), |
| }, |
| fsys::Entry { |
| key: "year".to_string(), |
| value: Some(Box::new(fsys::Value::Inum(2018))), |
| }, |
| ], |
| }))), |
| }, |
| ], |
| }), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_environment => { |
| input = json!({ |
| "environments": [ |
| { |
| "name": "myenv", |
| }, |
| { |
| "name": "myenv2", |
| "extends": "realm", |
| }, |
| { |
| "name": "myenv3", |
| "extends": "none", |
| "__stop_timeout_ms": 8000, |
| } |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| environments: Some(vec![ |
| fsys::EnvironmentDecl { |
| name: Some("myenv".to_string()), |
| extends: Some(fsys::EnvironmentExtends::None), |
| runners: None, |
| resolvers: None, |
| stop_timeout_ms: None, |
| ..fsys::EnvironmentDecl::EMPTY |
| }, |
| fsys::EnvironmentDecl { |
| name: Some("myenv2".to_string()), |
| extends: Some(fsys::EnvironmentExtends::Realm), |
| runners: None, |
| resolvers: None, |
| stop_timeout_ms: None, |
| ..fsys::EnvironmentDecl::EMPTY |
| }, |
| fsys::EnvironmentDecl { |
| name: Some("myenv3".to_string()), |
| extends: Some(fsys::EnvironmentExtends::None), |
| runners: None, |
| resolvers: None, |
| stop_timeout_ms: Some(8000), |
| ..fsys::EnvironmentDecl::EMPTY |
| }, |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_environment_with_runner_and_resolver => { |
| input = json!({ |
| "environments": [ |
| { |
| "name": "myenv", |
| "runners": [ |
| { |
| "runner": "dart", |
| "from": "parent", |
| } |
| ], |
| "resolvers": [ |
| { |
| "resolver": "pkg_resolver", |
| "from": "parent", |
| "scheme": "fuchsia-pkg", |
| } |
| ], |
| }, |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| environments: Some(vec![ |
| fsys::EnvironmentDecl { |
| name: Some("myenv".to_string()), |
| extends: Some(fsys::EnvironmentExtends::None), |
| runners: Some(vec![ |
| fsys::RunnerRegistration { |
| source_name: Some("dart".to_string()), |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("dart".to_string()), |
| ..fsys::RunnerRegistration::EMPTY |
| } |
| ]), |
| resolvers: Some(vec![ |
| fsys::ResolverRegistration { |
| resolver: Some("pkg_resolver".to_string()), |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| scheme: Some("fuchsia-pkg".to_string()), |
| ..fsys::ResolverRegistration::EMPTY |
| } |
| ]), |
| stop_timeout_ms: None, |
| ..fsys::EnvironmentDecl::EMPTY |
| }, |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_environment_with_runner_alias => { |
| input = json!({ |
| "environments": [ |
| { |
| "name": "myenv", |
| "runners": [ |
| { |
| "runner": "dart", |
| "from": "parent", |
| "as": "my-dart", |
| } |
| ], |
| }, |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| environments: Some(vec![ |
| fsys::EnvironmentDecl { |
| name: Some("myenv".to_string()), |
| extends: Some(fsys::EnvironmentExtends::None), |
| runners: Some(vec![ |
| fsys::RunnerRegistration { |
| source_name: Some("dart".to_string()), |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| target_name: Some("my-dart".to_string()), |
| ..fsys::RunnerRegistration::EMPTY |
| } |
| ]), |
| resolvers: None, |
| stop_timeout_ms: None, |
| ..fsys::EnvironmentDecl::EMPTY |
| }, |
| ]), |
| ..default_component_decl() |
| }, |
| }, |
| |
| test_compile_all_sections => { |
| input = json!({ |
| "program": { |
| "binary": "bin/app", |
| }, |
| "use": [ |
| { "service": "CoolFonts", "path": "/svc/fuchsia.fonts.Provider" }, |
| { "protocol": "LegacyCoolFonts", "path": "/svc/fuchsia.fonts.LegacyProvider" }, |
| { "protocol": [ "ReallyGoodFonts", "IWouldNeverUseTheseFonts"]}, |
| { "runner": "elf" }, |
| ], |
| "expose": [ |
| { "directory": "blobfs", "from": "self", "rights": ["r*"]}, |
| ], |
| "offer": [ |
| { |
| "service": "fuchsia.logger.Log", |
| "from": "#logger", |
| "to": [ "#netstack", "#modular" ] |
| }, |
| { |
| "protocol": "fuchsia.logger.LegacyLog", |
| "from": "#logger", |
| "to": [ "#netstack", "#modular" ], |
| "dependency": "weak_for_migration" |
| }, |
| ], |
| "children": [ |
| { |
| "name": "logger", |
| "url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm", |
| }, |
| { |
| "name": "netstack", |
| "url": "fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm", |
| }, |
| ], |
| "collections": [ |
| { |
| "name": "modular", |
| "durability": "persistent", |
| }, |
| ], |
| "capabilities": [ |
| { |
| "directory": "blobfs", |
| "path": "/volumes/blobfs", |
| "rights": [ "r*" ], |
| }, |
| { |
| "runner": "myrunner", |
| "path": "/runner", |
| "from": "self", |
| }, |
| ], |
| "facets": { |
| "author": "Fuchsia", |
| "year": 2018, |
| }, |
| "environments": [ |
| { |
| "name": "myenv", |
| "extends": "realm" |
| } |
| ], |
| }), |
| output = fsys::ComponentDecl { |
| program: Some(fdata::Dictionary { |
| entries: Some(vec![fdata::DictionaryEntry { |
| key: "binary".to_string(), |
| value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))), |
| }]), |
| ..fdata::Dictionary::EMPTY |
| }), |
| uses: Some(vec![ |
| fsys::UseDecl::Service ( |
| fsys::UseServiceDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("CoolFonts".to_string()), |
| target_path: Some("/svc/fuchsia.fonts.Provider".to_string()), |
| ..fsys::UseServiceDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Protocol ( |
| fsys::UseProtocolDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("LegacyCoolFonts".to_string()), |
| target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()), |
| ..fsys::UseProtocolDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Protocol ( |
| fsys::UseProtocolDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("ReallyGoodFonts".to_string()), |
| target_path: Some("/svc/ReallyGoodFonts".to_string()), |
| ..fsys::UseProtocolDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Protocol ( |
| fsys::UseProtocolDecl { |
| source: Some(fsys::Ref::Parent(fsys::ParentRef {})), |
| source_name: Some("IWouldNeverUseTheseFonts".to_string()), |
| target_path: Some("/svc/IWouldNeverUseTheseFonts".to_string()), |
| ..fsys::UseProtocolDecl::EMPTY |
| } |
| ), |
| fsys::UseDecl::Runner ( |
| fsys::UseRunnerDecl { |
| source_name: Some("elf".to_string()), |
| ..fsys::UseRunnerDecl::EMPTY |
| } |
| ), |
| ]), |
| exposes: Some(vec![ |
| fsys::ExposeDecl::Directory ( |
|