use crate::error::Error;
use crate::features::{Feature, FeatureSet};
use crate::validate::CapabilityRequirements;
use crate::{
offer_to_all_would_duplicate, validate, AnyRef, AsClause, Availability, Capability,
CapabilityClause, Child, Collection, ConfigKey, ConfigNestedValueType, ConfigRuntimeSource,
ConfigType, ConfigValueType, DebugRegistration, DictionaryRef, Document, Environment,
EnvironmentExtends, EnvironmentRef, EventScope, Expose, ExposeFromRef, ExposeToRef, FromClause,
Offer, OfferFromRef, OfferToRef, OneOrMany, Path, PathClause, Program, ResolverRegistration,
RightsClause, RootDictionaryRef, RunnerRegistration, SourceAvailability, Use, UseFromRef,
};
use cm_rust::NativeIntoFidl;
use cm_types::{self as cm, Name};
use indexmap::IndexMap;
use itertools::Itertools;
use serde_json::{Map, Value};
use sha2::{Digest, Sha256};
use std::collections::{BTreeMap, BTreeSet};
use std::convert::{Into, TryInto};
use std::path::PathBuf;
use {fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio};
#[derive(Default, Clone)]
pub struct CompileOptions<'a> {
file: Option<PathBuf>,
config_package_path: Option<String>,
features: Option<&'a FeatureSet>,
capability_requirements: CapabilityRequirements<'a>,
}
impl<'a> CompileOptions<'a> {
pub fn new() -> Self {
Default::default()
}
pub fn file(mut self, file: &std::path::Path) -> CompileOptions<'a> {
self.file = Some(file.to_path_buf());
self
}
pub fn config_package_path(mut self, config_package_path: &str) -> CompileOptions<'a> {
self.config_package_path = Some(config_package_path.to_string());
self
}
pub fn features(mut self, features: &'a FeatureSet) -> CompileOptions<'a> {
self.features = Some(features);
self
}
pub fn protocol_requirements(
mut self,
protocol_requirements: CapabilityRequirements<'a>,
) -> CompileOptions<'a> {
self.capability_requirements = protocol_requirements;
self
}
}
pub fn compile(
document: &Document,
options: CompileOptions<'_>,
) -> Result<fdecl::Component, Error> {
validate::validate_cml(
&document,
options.file.as_ref().map(PathBuf::as_path),
options.features.unwrap_or(&FeatureSet::empty()),
&options.capability_requirements,
)?;
let all_capability_names: BTreeSet<&Name> =
document.all_capability_names().into_iter().collect();
let all_children = document.all_children_names().into_iter().collect();
let all_collections = document.all_collection_names().into_iter().collect();
let component = fdecl::Component {
program: document.program.as_ref().map(translate_program).transpose()?,
uses: document
.r#use
.as_ref()
.map(|u| {
translate_use(&options, u, &all_capability_names, &all_children, &all_collections)
})
.transpose()?,
exposes: document
.expose
.as_ref()
.map(|e| {
translate_expose(
&options,
e,
&all_capability_names,
&all_collections,
&all_children,
)
})
.transpose()?,
offers: document
.offer
.as_ref()
.map(|offer| {
translate_offer(
&options,
offer,
&all_capability_names,
&all_children,
&all_collections,
)
})
.transpose()?,
capabilities: document
.capabilities
.as_ref()
.map(|c| translate_capabilities(&options, c, false))
.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(|env| translate_environments(&options, env, &all_capability_names))
.transpose()?,
facets: document.facets.clone().map(dictionary_from_nested_map).transpose()?,
config: translate_config(&document.config, &document.r#use, &options.config_package_path)?,
..Default::default()
};
cm_fidl_validator::validate(&component).map_err(Error::fidl_validator)?;
Ok(component)
}
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), ..Default::default() })
}
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)))),
Value::Array(arr) => {
if arr.iter().all(Value::is_string) {
let strs =
arr.into_iter().map(|v| v.as_str().unwrap().to_owned()).collect::<Vec<_>>();
Ok(Some(Box::new(fdata::DictionaryValue::StrVec(strs))))
} else if arr.iter().all(Value::is_object) {
let objs = arr
.into_iter()
.map(|v| v.as_object().unwrap().clone())
.map(|v| dictionary_from_nested_map(v.into_iter().collect()))
.collect::<Result<Vec<_>, _>>()?;
Ok(Some(Box::new(fdata::DictionaryValue::ObjVec(objs))))
} else {
Err(Error::validate(
"Values of an array must either exclusively strings or exclusively objects",
))
}
}
other => Err(Error::validate(format!(
"Value must be string, list of strings, or list of objects: {:?}",
other
))),
}
}
fn dictionary_from_nested_map(map: IndexMap<String, Value>) -> Result<fdata::Dictionary, Error> {
fn key_value_to_entries(
key: String,
value: Value,
) -> Result<Vec<fdata::DictionaryEntry>, Error> {
if let Value::Object(map) = value {
let entries = map
.into_iter()
.map(|(k, v)| key_value_to_entries([key.clone(), ".".to_string(), k].concat(), v))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect();
return Ok(entries);
}
let entry_value = value_to_dictionary_value(value)?;
Ok(vec![fdata::DictionaryEntry { key, value: entry_value }])
}
let entries = map
.into_iter()
.map(|(k, v)| key_value_to_entries(k, v))
.collect::<Result<Vec<_>, _>>()?
.into_iter()
.flatten()
.collect();
Ok(fdata::Dictionary { entries: Some(entries), ..Default::default() })
}
fn translate_program(program: &Program) -> Result<fdecl::Program, Error> {
Ok(fdecl::Program {
runner: program.runner.as_ref().map(|r| r.to_string()),
info: Some(dictionary_from_nested_map(program.info.clone())?),
..Default::default()
})
}
fn translate_use(
options: &CompileOptions<'_>,
use_in: &Vec<Use>,
all_capability_names: &BTreeSet<&Name>,
all_children: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
) -> Result<Vec<fdecl::Use>, Error> {
let mut out_uses = vec![];
for use_ in use_in {
if let Some(n) = use_.service() {
let (source, source_dictionary) =
extract_use_source(options, use_, all_capability_names, all_children)?;
let target_paths =
all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
let source_names = n.into_iter();
let availability = extract_use_availability(use_)?;
for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
{
out_uses.push(fdecl::Use::Service(fdecl::UseService {
source: Some(source.clone()),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: source_dictionary.clone(),
target_path: Some(target_path.into()),
dependency_type: Some(
use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
),
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = use_.protocol() {
let (source, source_dictionary) =
extract_use_source(options, use_, all_capability_names, all_children)?;
let target_paths =
all_target_use_paths(use_, use_).ok_or_else(|| Error::internal("no capability"))?;
let source_names = n.into_iter();
let availability = extract_use_availability(use_)?;
for (source_name, target_path) in source_names.into_iter().zip(target_paths.into_iter())
{
out_uses.push(fdecl::Use::Protocol(fdecl::UseProtocol {
source: Some(source.clone()),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: source_dictionary.clone(),
target_path: Some(target_path.into()),
dependency_type: Some(
use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
),
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = &use_.directory {
let (source, source_dictionary) =
extract_use_source(options, use_, all_capability_names, all_children)?;
let target_path = one_target_use_path(use_, use_)?;
let rights = extract_required_rights(use_, "use")?;
let subdir = extract_use_subdir(use_);
let availability = extract_use_availability(use_)?;
out_uses.push(fdecl::Use::Directory(fdecl::UseDirectory {
source: Some(source),
source_name: Some(n.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target_path: Some(target_path.into()),
rights: Some(rights),
subdir: subdir.map(|s| s.into()),
dependency_type: Some(
use_.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
),
availability: Some(availability),
..Default::default()
}));
} else if let Some(n) = &use_.storage {
let target_path = one_target_use_path(use_, use_)?;
let availability = extract_use_availability(use_)?;
out_uses.push(fdecl::Use::Storage(fdecl::UseStorage {
source_name: Some(n.clone().into()),
target_path: Some(target_path.into()),
availability: Some(availability),
..Default::default()
}));
} else if let Some(names) = &use_.event_stream {
let source_names: Vec<String> =
annotate_type::<Vec<cm_types::Name>>(names.clone().into())
.iter()
.map(|name| name.to_string())
.collect();
let availability = extract_use_availability(use_)?;
for name in source_names {
let scopes = match use_.scope.clone() {
Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
None => None,
};
let internal_error = format!("Internal error in all_target_use_paths when translating an EventStream. Please file a bug.");
let (source, _source_dictionary) =
extract_use_source(options, use_, all_capability_names, all_children)?;
out_uses.push(fdecl::Use::EventStream(fdecl::UseEventStream {
source_name: Some(name),
scope: match scopes {
Some(values) => {
let mut output = vec![];
for value in &values {
static EMPTY_SET: BTreeSet<&Name> = BTreeSet::new();
output.push(translate_target_ref(
options,
value.into(),
&all_children,
&all_collections,
&EMPTY_SET,
)?);
}
Some(output)
}
None => None,
},
source: Some(source),
target_path: Some(
annotate_type::<Vec<cm_types::Path>>(
all_target_use_paths(use_, use_)
.ok_or_else(|| Error::internal(internal_error.clone()))?
.into(),
)
.iter()
.next()
.ok_or_else(|| Error::internal(internal_error.clone()))?
.to_string(),
),
filter: match use_.filter.clone() {
Some(dict) => Some(dictionary_from_map(dict)?),
None => None,
},
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = &use_.runner {
let (source, source_dictionary) =
extract_use_source(&options, use_, all_capability_names, all_children)?;
#[cfg(fuchsia_api_level_at_least = "HEAD")]
out_uses.push(fdecl::Use::Runner(fdecl::UseRunner {
source: Some(source),
source_name: Some(n.clone().into()),
source_dictionary,
..Default::default()
}));
} else if let Some(n) = &use_.config {
let (source, source_dictionary) =
extract_use_source(&options, use_, all_capability_names, all_children)?;
let target = match &use_.key {
None => return Err(Error::validate("\"use config\" must have \"key\" field set.")),
Some(t) => t.clone(),
};
let availability = extract_use_availability(use_)?;
let type_ = validate::use_config_to_value_type(use_)?;
let default = if let Some(default) = &use_.config_default {
let value = config_value_file::field::config_value_from_json_value(
default,
&type_.clone().into(),
)
.map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", n, e)))?;
Some(value.native_into_fidl())
} else {
None
};
#[cfg(fuchsia_api_level_at_least = "20")]
out_uses.push(fdecl::Use::Config(fdecl::UseConfiguration {
source: Some(source),
source_name: Some(n.clone().into()),
target_name: Some(target.into()),
availability: Some(availability),
type_: Some(translate_value_type(&type_).0),
default,
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
..Default::default()
}));
} else {
return Err(Error::internal(format!("no capability in use declaration")));
};
}
Ok(out_uses)
}
fn translate_expose(
options: &CompileOptions<'_>,
expose_in: &Vec<Expose>,
all_capability_names: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
all_children: &BTreeSet<&Name>,
) -> Result<Vec<fdecl::Expose>, Error> {
let mut out_exposes = vec![];
for expose in expose_in.iter() {
let target = extract_expose_target(expose)?;
if let Some(source_names) = expose.service() {
let sources = extract_all_expose_sources(options, expose, Some(all_collections))?;
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())
{
for (source, source_dictionary) in &sources {
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
expose.availability.as_ref(),
source.clone(),
source_dictionary.clone(),
expose.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_exposes.push(fdecl::Expose::Service(fdecl::ExposeService {
source: Some(source),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target_name: Some(target_name.clone().into()),
target: Some(target.clone()),
availability: Some(availability),
..Default::default()
}))
}
}
} else if let Some(n) = expose.protocol() {
let (source, source_dictionary) =
extract_single_expose_source(options, expose, Some(all_capability_names))?;
let source_names = n.into_iter();
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())
{
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
expose.availability.as_ref(),
source.clone(),
source_dictionary.clone(),
expose.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_exposes.push(fdecl::Expose::Protocol(fdecl::ExposeProtocol {
source: Some(source),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target_name: Some(target_name.clone().into()),
target: Some(target.clone()),
availability: Some(availability),
..Default::default()
}))
}
} else if let Some(n) = expose.directory() {
let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
let source_names = n.into_iter();
let target_names = all_target_capability_names(expose, expose)
.ok_or_else(|| Error::internal("no capability"))?;
let rights = extract_expose_rights(expose)?;
let subdir = extract_expose_subdir(expose);
for (source_name, target_name) in source_names.into_iter().zip(target_names.into_iter())
{
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
expose.availability.as_ref(),
source.clone(),
source_dictionary.clone(),
expose.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_exposes.push(fdecl::Expose::Directory(fdecl::ExposeDirectory {
source: Some(source),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target_name: Some(target_name.clone().into()),
target: Some(target.clone()),
rights,
subdir: subdir.as_ref().map(|s| s.clone().into()),
availability: Some(availability),
..Default::default()
}))
}
} else if let Some(n) = expose.runner() {
let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
let source_names = n.into_iter();
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(fdecl::Expose::Runner(fdecl::ExposeRunner {
source: Some(source.clone()),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: source_dictionary.clone(),
target: Some(target.clone()),
target_name: Some(target_name.clone().into()),
..Default::default()
}))
}
} else if let Some(n) = expose.resolver() {
let (source, source_dictionary) = extract_single_expose_source(options, expose, None)?;
let source_names = n.into_iter();
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(fdecl::Expose::Resolver(fdecl::ExposeResolver {
source: Some(source.clone()),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: source_dictionary.clone(),
target: Some(target.clone()),
target_name: Some(target_name.clone().into()),
..Default::default()
}))
}
} else if let Some(n) = expose.dictionary() {
#[cfg(fuchsia_api_level_less_than = "25")]
{
return Err(Error::validate(format!(
"expose: dictionaries are not supported at this API level"
)));
}
#[cfg(fuchsia_api_level_at_least = "25")]
{
let (source, source_dictionary) =
extract_single_expose_source(options, expose, None)?;
let source_names = n.into_iter();
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())
{
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
expose.availability.as_ref(),
source.clone(),
source_dictionary.clone(),
expose.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_exposes.push(fdecl::Expose::Dictionary(fdecl::ExposeDictionary {
source: Some(source),
source_name: Some(source_name.clone().into()),
source_dictionary,
target_name: Some(target_name.clone().into()),
target: Some(target.clone()),
availability: Some(availability),
..Default::default()
}))
}
}
} else if let Some(n) = expose.config() {
#[cfg(fuchsia_api_level_less_than = "20")]
{
return Err(Error::validate(format!(
"expose: config blocks not supported at this API level"
)));
}
#[cfg(fuchsia_api_level_at_least = "20")]
{
let (source, source_dictionary) =
extract_single_expose_source(options, expose, None)?;
let source_names = n.into_iter();
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())
{
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
expose.availability.as_ref(),
source.clone(),
source_dictionary.clone(),
expose.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_exposes.push(fdecl::Expose::Config(fdecl::ExposeConfiguration {
source: Some(source.clone()),
source_name: Some(source_name.clone().into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target: Some(target.clone()),
target_name: Some(target_name.clone().into()),
availability: Some(availability),
..Default::default()
}))
}
}
} else {
return Err(Error::internal(format!("expose: must specify a known capability")));
}
}
Ok(out_exposes)
}
impl<T> Into<Vec<T>> for OneOrMany<T> {
fn into(self) -> Vec<T> {
match self {
OneOrMany::One(one) => vec![one],
OneOrMany::Many(many) => many,
}
}
}
fn annotate_type<T>(val: T) -> T {
val
}
struct DerivedSourceInfo {
source: fdecl::Ref,
source_dictionary: Option<String>,
availability: fdecl::Availability,
}
fn derive_source_and_availability(
availability: Option<&Availability>,
source: fdecl::Ref,
source_dictionary: Option<String>,
source_availability: Option<&SourceAvailability>,
all_capability_names: &BTreeSet<&Name>,
all_children: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
) -> DerivedSourceInfo {
let availability = availability.map(|a| match a {
Availability::Required => fdecl::Availability::Required,
Availability::Optional => fdecl::Availability::Optional,
Availability::SameAsTarget => fdecl::Availability::SameAsTarget,
Availability::Transitional => fdecl::Availability::Transitional,
});
if source_availability != Some(&SourceAvailability::Unknown) {
return DerivedSourceInfo {
source,
source_dictionary,
availability: availability.unwrap_or(fdecl::Availability::Required),
};
}
match &source {
fdecl::Ref::Child(fdecl::ChildRef { name, .. })
if !all_children.contains(&Name::new(name.clone()).unwrap()) =>
{
DerivedSourceInfo {
source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
source_dictionary: None,
availability: availability.unwrap_or(fdecl::Availability::Optional),
}
}
fdecl::Ref::Collection(fdecl::CollectionRef { name, .. })
if !all_collections.contains(&Name::new(name.clone()).unwrap()) =>
{
DerivedSourceInfo {
source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
source_dictionary: None,
availability: availability.unwrap_or(fdecl::Availability::Optional),
}
}
fdecl::Ref::Capability(fdecl::CapabilityRef { name, .. })
if !all_capability_names.contains(&Name::new(name.clone()).unwrap()) =>
{
DerivedSourceInfo {
source: fdecl::Ref::VoidType(fdecl::VoidRef {}),
source_dictionary: None,
availability: availability.unwrap_or(fdecl::Availability::Optional),
}
}
_ => DerivedSourceInfo {
source,
source_dictionary,
availability: availability.unwrap_or(fdecl::Availability::Required),
},
}
}
fn maybe_generate_direct_offer_from_all(
offer_to_all: &Offer,
direct_offers: &[Offer],
target: &cm_types::Name,
) -> Vec<Offer> {
assert!(offer_to_all.protocol.is_some() || offer_to_all.dictionary.is_some());
let mut returned_offers = vec![];
for mut local_offer in offer_to_all
.protocol
.as_ref()
.unwrap_or(&OneOrMany::Many(vec![]))
.iter()
.map(|individual_protocol| {
let mut local_offer = offer_to_all.clone();
local_offer.protocol = Some(OneOrMany::One(individual_protocol.clone()));
local_offer
})
.chain(
offer_to_all.dictionary.as_ref().unwrap_or(&OneOrMany::Many(vec![])).into_iter().map(
|dictionary| {
let mut local_offer = offer_to_all.clone();
local_offer.dictionary = Some(OneOrMany::One(dictionary.clone()));
local_offer
},
),
)
{
let disallowed_offer_source = OfferFromRef::Named(target.clone());
if direct_offers.iter().all(|direct| {
!offer_to_all_would_duplicate(&local_offer, direct, target).unwrap()
}) && !local_offer.from.iter().any(|from| from == &disallowed_offer_source)
{
local_offer.to = OneOrMany::One(OfferToRef::Named((*target).clone()));
returned_offers.push(local_offer);
}
}
returned_offers
}
fn expand_offer_to_all(
offers_in: &Vec<Offer>,
children: &BTreeSet<&Name>,
collections: &BTreeSet<&Name>,
) -> Result<Vec<Offer>, Error> {
let offers_to_all =
offers_in.iter().filter(|offer| matches!(offer.to, OneOrMany::One(OfferToRef::All)));
let mut direct_offers = offers_in
.iter()
.filter(|o| !matches!(o.to, OneOrMany::One(OfferToRef::All)))
.map(Offer::clone)
.collect::<Vec<Offer>>();
for offer_to_all in offers_to_all {
for target in children.iter().chain(collections.iter()) {
let offers = maybe_generate_direct_offer_from_all(offer_to_all, &direct_offers, target);
for offer in offers {
direct_offers.push(offer);
}
}
}
Ok(direct_offers)
}
fn translate_offer(
options: &CompileOptions<'_>,
offer_in: &Vec<Offer>,
all_capability_names: &BTreeSet<&Name>,
all_children: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
) -> Result<Vec<fdecl::Offer>, Error> {
let mut out_offers = vec![];
let expanded_offers = expand_offer_to_all(offer_in, all_children, all_collections)?;
for offer in &expanded_offers {
if let Some(n) = offer.service() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_offers.push(fdecl::Offer::Service(fdecl::OfferService {
source: Some(source),
source_name: Some(source_name.into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target: Some(target),
target_name: Some(target_name.into()),
availability: Some(availability),
#[cfg(fuchsia_api_level_at_least = "HEAD")]
dependency_type: Some(
offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
),
..Default::default()
}));
}
} else if let Some(n) = offer.protocol() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_offers.push(fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(source),
source_name: Some(source_name.into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target: Some(target),
target_name: Some(target_name.into()),
dependency_type: Some(
offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
),
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = offer.directory() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_offers.push(fdecl::Offer::Directory(fdecl::OfferDirectory {
source: Some(source),
source_name: Some(source_name.into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target: Some(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(),
),
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = offer.storage() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary: _, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_offers.push(fdecl::Offer::Storage(fdecl::OfferStorage {
source: Some(source),
source_name: Some(source_name.into()),
target: Some(target),
target_name: Some(target_name.into()),
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = offer.runner() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
out_offers.push(fdecl::Offer::Runner(fdecl::OfferRunner {
source: Some(source),
source_name: Some(source_name.into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target: Some(target),
target_name: Some(target_name.into()),
..Default::default()
}));
}
} else if let Some(n) = offer.resolver() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
out_offers.push(fdecl::Offer::Resolver(fdecl::OfferResolver {
source: Some(source),
source_name: Some(source_name.into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
target: Some(target),
target_name: Some(target_name.into()),
..Default::default()
}));
}
} else if let Some(n) = offer.event_stream() {
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary: _, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
let scopes = match offer.scope.clone() {
Some(value) => Some(annotate_type::<Vec<EventScope>>(value.into())),
None => None,
};
out_offers.push(fdecl::Offer::EventStream(fdecl::OfferEventStream {
source: Some(source),
source_name: Some(source_name.into()),
target: Some(target),
target_name: Some(target_name.into()),
scope: match scopes {
Some(values) => {
let mut output = vec![];
for value in &values {
static EMPTY_SET: BTreeSet<&Name> = BTreeSet::new();
output.push(translate_target_ref(
options,
value.into(),
&all_children,
&all_collections,
&EMPTY_SET,
)?);
}
Some(output)
}
None => None,
},
availability: Some(availability),
..Default::default()
}));
}
} else if let Some(n) = offer.dictionary() {
#[cfg(fuchsia_api_level_less_than = "25")]
{
return Err(Error::validate(format!(
"offer: dictionaries are not supported at this API level"
)));
}
#[cfg(fuchsia_api_level_at_least = "25")]
{
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_offers.push(fdecl::Offer::Dictionary(fdecl::OfferDictionary {
source: Some(source),
source_name: Some(source_name.into()),
source_dictionary,
target: Some(target),
target_name: Some(target_name.into()),
dependency_type: Some(
offer.dependency.clone().unwrap_or(cm::DependencyType::Strong).into(),
),
availability: Some(availability),
..Default::default()
}));
}
}
} else if let Some(n) = offer.config() {
#[cfg(fuchsia_api_level_less_than = "20")]
{
return Err(Error::validate(format!(
"offer: config blocks not supported at this API level"
)));
}
#[cfg(fuchsia_api_level_at_least = "20")]
{
let entries = extract_offer_sources_and_targets(
options,
offer,
n,
all_capability_names,
all_children,
all_collections,
)?;
for (source, source_dictionary, source_name, target, target_name) in entries {
let DerivedSourceInfo { source, source_dictionary, availability } =
derive_source_and_availability(
offer.availability.as_ref(),
source,
source_dictionary,
offer.source_availability.as_ref(),
all_capability_names,
all_children,
all_collections,
);
out_offers.push(fdecl::Offer::Config(fdecl::OfferConfiguration {
source: Some(source),
source_name: Some(source_name.into()),
target: Some(target),
target_name: Some(target_name.into()),
availability: Some(availability),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary,
..Default::default()
}));
}
}
} else {
return Err(Error::internal(format!("no capability")));
}
}
Ok(out_offers)
}
fn translate_children(children_in: &Vec<Child>) -> Result<Vec<fdecl::Child>, Error> {
let mut out_children = vec![];
for child in children_in.iter() {
out_children.push(fdecl::Child {
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()),
on_terminate: child.on_terminate.as_ref().map(|r| r.clone().into()),
..Default::default()
});
}
Ok(out_children)
}
fn translate_collections(
collections_in: &Vec<Collection>,
) -> Result<Vec<fdecl::Collection>, Error> {
let mut out_collections = vec![];
for collection in collections_in.iter() {
out_collections.push(fdecl::Collection {
name: Some(collection.name.clone().into()),
durability: Some(collection.durability.clone().into()),
environment: extract_environment_ref(collection.environment.as_ref()).map(|e| e.into()),
allowed_offers: collection.allowed_offers.clone().map(|a| a.into()),
allow_long_names: collection.allow_long_names.clone(),
persistent_storage: collection.persistent_storage.clone(),
..Default::default()
});
}
Ok(out_collections)
}
fn translate_nested_value_type(nested_type: &ConfigNestedValueType) -> fdecl::ConfigType {
let layout = match nested_type {
ConfigNestedValueType::Bool {} => fdecl::ConfigTypeLayout::Bool,
ConfigNestedValueType::Uint8 {} => fdecl::ConfigTypeLayout::Uint8,
ConfigNestedValueType::Uint16 {} => fdecl::ConfigTypeLayout::Uint16,
ConfigNestedValueType::Uint32 {} => fdecl::ConfigTypeLayout::Uint32,
ConfigNestedValueType::Uint64 {} => fdecl::ConfigTypeLayout::Uint64,
ConfigNestedValueType::Int8 {} => fdecl::ConfigTypeLayout::Int8,
ConfigNestedValueType::Int16 {} => fdecl::ConfigTypeLayout::Int16,
ConfigNestedValueType::Int32 {} => fdecl::ConfigTypeLayout::Int32,
ConfigNestedValueType::Int64 {} => fdecl::ConfigTypeLayout::Int64,
ConfigNestedValueType::String { .. } => fdecl::ConfigTypeLayout::String,
};
let constraints = match nested_type {
ConfigNestedValueType::String { max_size } => {
vec![fdecl::LayoutConstraint::MaxSize(max_size.get())]
}
_ => vec![],
};
fdecl::ConfigType {
layout,
constraints,
parameters: Some(vec![]),
}
}
fn translate_value_type(
value_type: &ConfigValueType,
) -> (fdecl::ConfigType, fdecl::ConfigMutability) {
let (layout, source_mutability) = match value_type {
ConfigValueType::Bool { mutability } => (fdecl::ConfigTypeLayout::Bool, mutability),
ConfigValueType::Uint8 { mutability } => (fdecl::ConfigTypeLayout::Uint8, mutability),
ConfigValueType::Uint16 { mutability } => (fdecl::ConfigTypeLayout::Uint16, mutability),
ConfigValueType::Uint32 { mutability } => (fdecl::ConfigTypeLayout::Uint32, mutability),
ConfigValueType::Uint64 { mutability } => (fdecl::ConfigTypeLayout::Uint64, mutability),
ConfigValueType::Int8 { mutability } => (fdecl::ConfigTypeLayout::Int8, mutability),
ConfigValueType::Int16 { mutability } => (fdecl::ConfigTypeLayout::Int16, mutability),
ConfigValueType::Int32 { mutability } => (fdecl::ConfigTypeLayout::Int32, mutability),
ConfigValueType::Int64 { mutability } => (fdecl::ConfigTypeLayout::Int64, mutability),
ConfigValueType::String { mutability, .. } => (fdecl::ConfigTypeLayout::String, mutability),
ConfigValueType::Vector { mutability, .. } => (fdecl::ConfigTypeLayout::Vector, mutability),
};
let (constraints, parameters) = match value_type {
ConfigValueType::String { max_size, .. } => {
(vec![fdecl::LayoutConstraint::MaxSize(max_size.get())], vec![])
}
ConfigValueType::Vector { max_count, element, .. } => {
let nested_type = translate_nested_value_type(element);
(
vec![fdecl::LayoutConstraint::MaxSize(max_count.get())],
vec![fdecl::LayoutParameter::NestedType(nested_type)],
)
}
_ => (vec![], vec![]),
};
let mut mutability = fdecl::ConfigMutability::empty();
if let Some(source_mutability) = source_mutability {
for source in source_mutability {
match source {
ConfigRuntimeSource::Parent => mutability |= fdecl::ConfigMutability::PARENT,
}
}
}
(
fdecl::ConfigType {
layout,
constraints,
parameters: Some(parameters),
},
mutability,
)
}
fn translate_config(
fields: &Option<BTreeMap<ConfigKey, ConfigValueType>>,
uses: &Option<Vec<Use>>,
package_path: &Option<String>,
) -> Result<Option<fdecl::ConfigSchema>, Error> {
let mut use_fields: BTreeMap<ConfigKey, ConfigValueType> = uses
.iter()
.flatten()
.map(|u| {
if u.config.is_none() {
return None;
}
let key = ConfigKey(u.key.clone().expect("key should be set").into());
let config_type =
validate::use_config_to_value_type(u).expect("config type should be valid");
Some((key, config_type))
})
.flatten()
.collect();
for (key, value) in fields.iter().flatten() {
if use_fields.contains_key(key) {
if use_fields.get(key) != Some(&value) {
return Err(Error::validate(format!(
"Config error: `use` and `config` block contain key '{}' with different types",
key
)));
}
}
use_fields.insert(key.clone(), value.clone());
}
if use_fields.is_empty() {
return Ok(None);
}
let source = match fields.as_ref().map_or(true, |f| f.is_empty()) {
#[cfg(fuchsia_api_level_at_least = "20")]
true => fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default()),
_ => {
let Some(package_path) = package_path.as_ref() else {
return Err(Error::invalid_args(
"can't translate config: no package path for value file",
));
};
fdecl::ConfigValueSource::PackagePath(package_path.to_owned())
}
};
let mut fidl_fields = vec![];
let mut hasher = Sha256::new();
for (key, value) in &use_fields {
let (type_, mutability) = translate_value_type(value);
fidl_fields.push(fdecl::ConfigField {
key: Some(key.to_string()),
type_: Some(type_),
mutability: Some(mutability),
..Default::default()
});
hasher.update(key.as_str());
value.update_digest(&mut hasher);
}
let hash = hasher.finalize();
let checksum = fdecl::ConfigChecksum::Sha256(*hash.as_ref());
Ok(Some(fdecl::ConfigSchema {
fields: Some(fidl_fields),
checksum: Some(checksum),
value_source: Some(source),
..Default::default()
}))
}
fn translate_environments(
options: &CompileOptions<'_>,
envs_in: &Vec<Environment>,
all_capability_names: &BTreeSet<&Name>,
) -> Result<Vec<fdecl::Environment>, Error> {
envs_in
.iter()
.map(|env| {
Ok(fdecl::Environment {
name: Some(env.name.clone().into()),
extends: match env.extends {
Some(EnvironmentExtends::Realm) => Some(fdecl::EnvironmentExtends::Realm),
Some(EnvironmentExtends::None) => Some(fdecl::EnvironmentExtends::None),
None => Some(fdecl::EnvironmentExtends::None),
},
runners: env
.runners
.as_ref()
.map(|runners| {
runners
.iter()
.map(|r| translate_runner_registration(options, r))
.collect::<Result<Vec<_>, Error>>()
})
.transpose()?,
resolvers: env
.resolvers
.as_ref()
.map(|resolvers| {
resolvers
.iter()
.map(|r| translate_resolver_registration(options, r))
.collect::<Result<Vec<_>, Error>>()
})
.transpose()?,
debug_capabilities: env
.debug
.as_ref()
.map(|debug_capabiltities| {
translate_debug_capabilities(
options,
debug_capabiltities,
all_capability_names,
)
})
.transpose()?,
stop_timeout_ms: env.stop_timeout_ms.map(|s| s.0),
..Default::default()
})
})
.collect()
}
fn translate_runner_registration(
options: &CompileOptions<'_>,
reg: &RunnerRegistration,
) -> Result<fdecl::RunnerRegistration, Error> {
let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
Ok(fdecl::RunnerRegistration {
source_name: Some(reg.runner.clone().into()),
source: Some(source),
target_name: Some(reg.r#as.as_ref().unwrap_or(®.runner).clone().into()),
..Default::default()
})
}
fn translate_resolver_registration(
options: &CompileOptions<'_>,
reg: &ResolverRegistration,
) -> Result<fdecl::ResolverRegistration, Error> {
let (source, _source_dictionary) = extract_single_offer_source(options, reg, None)?;
Ok(fdecl::ResolverRegistration {
resolver: Some(reg.resolver.clone().into()),
source: Some(source),
scheme: Some(
reg.scheme
.as_str()
.parse::<cm_types::UrlScheme>()
.map_err(|e| Error::internal(format!("invalid URL scheme: {}", e)))?
.into(),
),
..Default::default()
})
}
fn translate_debug_capabilities(
options: &CompileOptions<'_>,
capabilities: &Vec<DebugRegistration>,
all_capability_names: &BTreeSet<&Name>,
) -> Result<Vec<fdecl::DebugRegistration>, Error> {
let mut out_capabilities = vec![];
for capability in capabilities {
if let Some(n) = capability.protocol() {
let (source, _source_dictionary) =
extract_single_offer_source(options, capability, Some(all_capability_names))?;
let targets = all_target_capability_names(capability, capability)
.ok_or_else(|| Error::internal("no capability"))?;
let source_names = n;
for target_name in targets {
let source_name = if source_names.len() == 1 {
(*source_names.iter().next().unwrap()).clone()
} else {
target_name.clone()
};
out_capabilities.push(fdecl::DebugRegistration::Protocol(
fdecl::DebugProtocolRegistration {
source: Some(source.clone()),
source_name: Some(source_name.into()),
target_name: Some(target_name.clone().into()),
..Default::default()
},
));
}
}
}
Ok(out_capabilities)
}
fn extract_use_source(
options: &CompileOptions<'_>,
in_obj: &Use,
all_capability_names: &BTreeSet<&Name>,
all_children_names: &BTreeSet<&Name>,
) -> Result<(fdecl::Ref, Option<String>), Error> {
let ref_ = match in_obj.from.as_ref() {
Some(UseFromRef::Parent) => fdecl::Ref::Parent(fdecl::ParentRef {}),
Some(UseFromRef::Framework) => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
Some(UseFromRef::Debug) => fdecl::Ref::Debug(fdecl::DebugRef {}),
Some(UseFromRef::Self_) => fdecl::Ref::Self_(fdecl::SelfRef {}),
Some(UseFromRef::Named(name)) => {
if all_capability_names.contains(&name) {
fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
} else if all_children_names.contains(&name) {
fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
} else {
return Err(Error::internal(format!(
"use source \"{:?}\" not supported for \"use from\"",
name
)));
}
}
Some(UseFromRef::Dictionary(d)) => {
return dictionary_ref_to_source(&d);
}
None => fdecl::Ref::Parent(fdecl::ParentRef {}), };
Ok((ref_, None))
}
fn extract_use_availability(in_obj: &Use) -> Result<fdecl::Availability, Error> {
match in_obj.availability.as_ref() {
Some(Availability::Required) | None => Ok(fdecl::Availability::Required),
Some(Availability::Optional) => Ok(fdecl::Availability::Optional),
Some(Availability::Transitional) => Ok(fdecl::Availability::Transitional),
Some(Availability::SameAsTarget) => Err(Error::internal(
"availability \"same_as_target\" not supported for use declarations",
)),
}
}
fn extract_use_subdir(in_obj: &Use) -> Option<cm::RelativePath> {
in_obj.subdir.clone()
}
fn extract_expose_subdir(in_obj: &Expose) -> Option<cm::RelativePath> {
in_obj.subdir.clone()
}
fn extract_offer_subdir(in_obj: &Offer) -> Option<cm::RelativePath> {
in_obj.subdir.clone()
}
fn extract_expose_rights(in_obj: &Expose) -> Result<Option<fio::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 = BTreeSet::new();
let mut operations: fio::Operations = fio::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))
}
None => Ok(None),
}
}
fn expose_source_from_ref(
options: &CompileOptions<'_>,
reference: &ExposeFromRef,
all_capability_names: Option<&BTreeSet<&Name>>,
all_collections: Option<&BTreeSet<&Name>>,
) -> Result<(fdecl::Ref, Option<String>), Error> {
let ref_ = match reference {
ExposeFromRef::Named(name) => {
if all_capability_names.is_some() && all_capability_names.unwrap().contains(&name) {
fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
} else if all_collections.is_some() && all_collections.unwrap().contains(&name) {
fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() })
} else {
fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
}
}
ExposeFromRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
ExposeFromRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
ExposeFromRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
ExposeFromRef::Dictionary(d) => {
return dictionary_ref_to_source(&d);
}
};
Ok((ref_, None))
}
fn extract_single_expose_source(
options: &CompileOptions<'_>,
in_obj: &Expose,
all_capability_names: Option<&BTreeSet<&Name>>,
) -> Result<(fdecl::Ref, Option<String>), Error> {
match &in_obj.from {
OneOrMany::One(reference) => {
expose_source_from_ref(options, &reference, all_capability_names, None)
}
OneOrMany::Many(many) => {
return Err(Error::internal(format!(
"multiple unexpected \"from\" clauses for \"expose\": {:?}",
many
)))
}
}
}
fn extract_all_expose_sources(
options: &CompileOptions<'_>,
in_obj: &Expose,
all_collections: Option<&BTreeSet<&Name>>,
) -> Result<Vec<(fdecl::Ref, Option<String>)>, Error> {
in_obj.from.iter().map(|e| expose_source_from_ref(options, e, None, all_collections)).collect()
}
fn extract_offer_rights(in_obj: &Offer) -> Result<Option<fio::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 = BTreeSet::new();
let mut operations: fio::Operations = fio::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))
}
None => Ok(None),
}
}
fn extract_single_offer_source<T>(
options: &CompileOptions<'_>,
in_obj: &T,
all_capability_names: Option<&BTreeSet<&Name>>,
) -> Result<(fdecl::Ref, Option<String>), Error>
where
T: FromClause,
{
match in_obj.from_() {
OneOrMany::One(reference) => {
any_ref_to_decl(options, reference, all_capability_names, None)
}
many => {
return Err(Error::internal(format!(
"multiple unexpected \"from\" clauses for \"offer\": {}",
many
)))
}
}
}
fn extract_all_offer_sources<T: FromClause>(
options: &CompileOptions<'_>,
in_obj: &T,
all_capability_names: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
) -> Result<Vec<(fdecl::Ref, Option<String>)>, Error> {
in_obj
.from_()
.into_iter()
.map(|r| {
any_ref_to_decl(options, r.clone(), Some(all_capability_names), Some(all_collections))
})
.collect()
}
fn translate_target_ref(
options: &CompileOptions<'_>,
reference: AnyRef<'_>,
all_children: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
all_capabilities: &BTreeSet<&Name>,
) -> Result<fdecl::Ref, Error> {
match reference {
AnyRef::Named(name) if all_children.contains(name) => {
Ok(fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None }))
}
AnyRef::Named(name) if all_collections.contains(name) => {
Ok(fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() }))
}
AnyRef::Named(name) if all_capabilities.contains(name) => {
Ok(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() }))
}
AnyRef::OwnDictionary(name) if all_capabilities.contains(name) => {
#[cfg(fuchsia_api_level_at_least = "25")]
return Ok(fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() }));
#[cfg(fuchsia_api_level_less_than = "25")]
return Err(Error::validate("dictionaries are not supported at this API level"));
}
AnyRef::Named(_) => Err(Error::internal(format!("dangling reference: \"{}\"", reference))),
_ => Err(Error::internal(format!("invalid child reference: \"{}\"", reference))),
}
}
fn extract_offer_sources_and_targets(
options: &CompileOptions<'_>,
offer: &Offer,
source_names: OneOrMany<&Name>,
all_capability_names: &BTreeSet<&Name>,
all_children: &BTreeSet<&Name>,
all_collections: &BTreeSet<&Name>,
) -> Result<Vec<(fdecl::Ref, Option<String>, Name, fdecl::Ref, Name)>, Error> {
let mut out = vec![];
let sources = extract_all_offer_sources(options, offer, all_capability_names, all_collections)?;
let target_names = all_target_capability_names(offer, offer)
.ok_or_else(|| Error::internal("no capability".to_string()))?;
for (source, source_dictionary) in sources {
for to in &offer.to {
for target_name in &target_names {
let source_name = if source_names.len() == 1 {
(*source_names.iter().next().unwrap()).clone()
} else {
(*target_name).clone()
};
let target = translate_target_ref(
options,
to.into(),
all_children,
all_collections,
all_capability_names,
)?;
out.push((
source.clone(),
source_dictionary.clone(),
source_name,
target,
(*target_name).clone(),
))
}
}
}
Ok(out)
}
fn all_target_use_paths<T, U>(in_obj: &T, to_obj: &U) -> Option<OneOrMany<Path>>
where
T: CapabilityClause,
U: PathClause,
{
if let Some(n) = in_obj.service() {
Some(svc_paths_from_names(n, to_obj))
} else if let Some(n) = in_obj.protocol() {
Some(svc_paths_from_names(n, to_obj))
} 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 default_path = Path::new("/svc/fuchsia.component.EventStream").unwrap();
let path = to_obj.path().unwrap_or(&default_path);
Some(OneOrMany::One(path.clone()))
} else {
None
}
}
fn svc_paths_from_names<T>(names: OneOrMany<&Name>, to_obj: &T) -> OneOrMany<Path>
where
T: PathClause,
{
match names {
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)
}
}
}
fn one_target_use_path<T, U>(in_obj: &T, to_obj: &U) -> Result<Path, Error>
where
T: CapabilityClause,
U: 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")),
}
}
fn all_target_capability_names<'a, T, U>(
in_obj: &'a T,
to_obj: &'a U,
) -> Option<OneOrMany<&'a Name>>
where
T: CapabilityClause,
U: AsClause + PathClause,
{
if let Some(as_) = to_obj.r#as() {
Some(OneOrMany::One(as_))
} else {
if let Some(n) = in_obj.service() {
Some(n)
} else if let Some(n) = in_obj.protocol() {
Some(n)
} else if let Some(n) = in_obj.directory() {
Some(n)
} else if let Some(n) = in_obj.storage() {
Some(n)
} else if let Some(n) = in_obj.runner() {
Some(n)
} else if let Some(n) = in_obj.resolver() {
Some(n)
} else if let Some(n) = in_obj.event_stream() {
Some(n)
} else if let Some(n) = in_obj.dictionary() {
Some(n)
} else if let Some(n) = in_obj.config() {
Some(n)
} else {
None
}
}
}
fn extract_expose_target(in_obj: &Expose) -> Result<fdecl::Ref, Error> {
match &in_obj.to {
Some(ExposeToRef::Parent) => Ok(fdecl::Ref::Parent(fdecl::ParentRef {})),
Some(ExposeToRef::Framework) => Ok(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
None => Ok(fdecl::Ref::Parent(fdecl::ParentRef {})),
}
}
fn extract_environment_ref(r: Option<&EnvironmentRef>) -> Option<cm::Name> {
r.map(|r| {
let EnvironmentRef::Named(name) = r;
name.clone()
})
}
pub fn translate_capabilities(
options: &CompileOptions<'_>,
capabilities_in: &Vec<Capability>,
as_builtin: bool,
) -> Result<Vec<fdecl::Capability>, Error> {
let mut out_capabilities = vec![];
for capability in capabilities_in {
if let Some(service) = &capability.service {
for n in service.iter() {
let source_path = match as_builtin {
true => None,
false => Some(
capability
.path
.clone()
.unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
.into(),
),
};
out_capabilities.push(fdecl::Capability::Service(fdecl::Service {
name: Some(n.clone().into()),
source_path,
..Default::default()
}));
}
} else if let Some(protocol) = &capability.protocol {
for n in protocol.iter() {
let source_path = match as_builtin {
true => None,
false => Some(
capability
.path
.clone()
.unwrap_or_else(|| format!("/svc/{}", n).parse().unwrap())
.into(),
),
};
out_capabilities.push(fdecl::Capability::Protocol(fdecl::Protocol {
name: Some(n.clone().into()),
source_path,
#[cfg(fuchsia_api_level_at_least = "HEAD")]
delivery: capability.delivery.map(Into::into),
..Default::default()
}));
}
} else if let Some(n) = &capability.directory {
let source_path = match as_builtin {
true => None,
false => {
Some(capability.path.as_ref().expect("missing source path").clone().into())
}
};
let rights = extract_required_rights(capability, "capability")?;
out_capabilities.push(fdecl::Capability::Directory(fdecl::Directory {
name: Some(n.clone().into()),
source_path,
rights: Some(rights),
..Default::default()
}));
} else if let Some(n) = &capability.storage {
if as_builtin {
return Err(Error::internal(format!(
"built-in storage capabilities are not supported"
)));
}
let backing_dir = capability
.backing_dir
.as_ref()
.expect("storage has no path or backing_dir")
.clone()
.into();
let (source, _source_dictionary) =
any_ref_to_decl(options, capability.from.as_ref().unwrap().into(), None, None)?;
out_capabilities.push(fdecl::Capability::Storage(fdecl::Storage {
name: Some(n.clone().into()),
backing_dir: Some(backing_dir),
subdir: capability.subdir.clone().map(Into::into),
source: Some(source),
storage_id: Some(
capability.storage_id.clone().expect("storage is missing storage_id").into(),
),
..Default::default()
}));
} else if let Some(n) = &capability.runner {
let source_path = match as_builtin {
true => None,
false => {
Some(capability.path.as_ref().expect("missing source path").clone().into())
}
};
out_capabilities.push(fdecl::Capability::Runner(fdecl::Runner {
name: Some(n.clone().into()),
source_path,
..Default::default()
}));
} else if let Some(n) = &capability.resolver {
let source_path = match as_builtin {
true => None,
false => {
Some(capability.path.as_ref().expect("missing source path").clone().into())
}
};
out_capabilities.push(fdecl::Capability::Resolver(fdecl::Resolver {
name: Some(n.clone().into()),
source_path,
..Default::default()
}));
} else if let Some(ns) = &capability.event_stream {
if !as_builtin {
return Err(Error::internal(format!(
"event_stream capabilities may only be declared as built-in capabilities"
)));
}
for n in ns {
out_capabilities.push(fdecl::Capability::EventStream(fdecl::EventStream {
name: Some(n.clone().into()),
..Default::default()
}));
}
} else if let Some(n) = &capability.dictionary {
#[cfg(fuchsia_api_level_less_than = "25")]
{
return Err(Error::validate(format!(
"dictionary capabilities are not supported at this API level"
)));
}
#[cfg(fuchsia_api_level_at_least = "25")]
{
out_capabilities.push(fdecl::Capability::Dictionary(fdecl::Dictionary {
name: Some(n.clone().into()),
source_path: capability.path.clone().map(Into::into),
..Default::default()
}));
}
} else if let Some(c) = &capability.config {
#[cfg(fuchsia_api_level_less_than = "20")]
{
return Err(Error::validate(format!(
"configuration capabilities are not supported at this API level"
)));
}
#[cfg(fuchsia_api_level_at_least = "20")]
{
let value = configuration_to_value(
c,
&capability,
&capability.config_type,
&capability.value,
)?;
out_capabilities.push(fdecl::Capability::Config(fdecl::Configuration {
name: Some(c.clone().into()),
value: Some(value),
..Default::default()
}));
}
} else {
return Err(Error::internal(format!("no capability declaration recognized")));
}
}
Ok(out_capabilities)
}
pub fn extract_required_rights<T>(in_obj: &T, keyword: &str) -> Result<fio::Operations, Error>
where
T: RightsClause,
{
match in_obj.rights() {
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(format!(
"Rights provided to `{}` are not well formed.",
keyword
)));
}
let mut seen_rights = BTreeSet::new();
let mut operations: fio::Operations = fio::Operations::empty();
for right in rights.iter() {
if seen_rights.contains(&right) {
return Err(Error::duplicate_rights(format!(
"Rights provided to `{}` are not well formed.",
keyword
)));
}
seen_rights.insert(right);
operations |= *right;
}
Ok(operations)
}
None => Err(Error::internal(format!(
"No `{}` rights provided but required for directories",
keyword
))),
}
}
pub fn any_ref_to_decl(
options: &CompileOptions<'_>,
reference: AnyRef<'_>,
all_capability_names: Option<&BTreeSet<&Name>>,
all_collection_names: Option<&BTreeSet<&Name>>,
) -> Result<(fdecl::Ref, Option<String>), Error> {
let ref_ = match reference {
AnyRef::Named(name) => {
if all_capability_names.is_some() && all_capability_names.unwrap().contains(&name) {
fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
} else if all_collection_names.is_some()
&& all_collection_names.unwrap().contains(&name)
{
fdecl::Ref::Collection(fdecl::CollectionRef { name: name.clone().into() })
} else {
fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
}
}
AnyRef::Framework => fdecl::Ref::Framework(fdecl::FrameworkRef {}),
AnyRef::Debug => fdecl::Ref::Debug(fdecl::DebugRef {}),
AnyRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
AnyRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
AnyRef::Void => fdecl::Ref::VoidType(fdecl::VoidRef {}),
AnyRef::Dictionary(d) => {
return dictionary_ref_to_source(&d);
}
AnyRef::OwnDictionary(name) => {
#[cfg(fuchsia_api_level_at_least = "25")]
{
fdecl::Ref::Capability(fdecl::CapabilityRef { name: name.clone().into() })
}
#[cfg(fuchsia_api_level_less_than = "25")]
return Err(Error::validate("dictionaries are not supported at this API level"));
}
};
Ok((ref_, None))
}
fn dictionary_ref_to_source(d: &DictionaryRef) -> Result<(fdecl::Ref, Option<String>), Error> {
#[allow(unused)]
let root = match &d.root {
RootDictionaryRef::Named(name) => {
fdecl::Ref::Child(fdecl::ChildRef { name: name.clone().into(), collection: None })
}
RootDictionaryRef::Parent => fdecl::Ref::Parent(fdecl::ParentRef {}),
RootDictionaryRef::Self_ => fdecl::Ref::Self_(fdecl::SelfRef {}),
};
#[cfg(fuchsia_api_level_at_least = "25")]
return Ok((root, Some(d.path.to_string())));
#[cfg(fuchsia_api_level_less_than = "25")]
return Err(Error::validate("dictionaries are not supported at this API level"));
}
fn configuration_to_value(
name: &Name,
capability: &Capability,
config_type: &Option<ConfigType>,
value: &Option<serde_json::Value>,
) -> Result<fdecl::ConfigValue, Error> {
let Some(config_type) = config_type.as_ref() else {
return Err(Error::InvalidArgs(format!(
"Configuration field '{}' must have 'type' set",
name
)));
};
let Some(value) = value.as_ref() else {
return Err(Error::InvalidArgs(format!(
"Configuration field '{}' must have 'value' set",
name
)));
};
let config_type = match config_type {
ConfigType::Bool => cm_rust::ConfigValueType::Bool,
ConfigType::Uint8 => cm_rust::ConfigValueType::Uint8,
ConfigType::Uint16 => cm_rust::ConfigValueType::Uint16,
ConfigType::Uint32 => cm_rust::ConfigValueType::Uint32,
ConfigType::Uint64 => cm_rust::ConfigValueType::Uint64,
ConfigType::Int8 => cm_rust::ConfigValueType::Int8,
ConfigType::Int16 => cm_rust::ConfigValueType::Int16,
ConfigType::Int32 => cm_rust::ConfigValueType::Int32,
ConfigType::Int64 => cm_rust::ConfigValueType::Int64,
ConfigType::String => {
let Some(max_size) = capability.config_max_size else {
return Err(Error::InvalidArgs(format!(
"Configuration field '{}' must have 'max_size' set",
name
)));
};
cm_rust::ConfigValueType::String { max_size: max_size.into() }
}
ConfigType::Vector => {
let Some(ref element) = capability.config_element_type else {
return Err(Error::InvalidArgs(format!(
"Configuration field '{}' must have 'element_type' set",
name
)));
};
let Some(max_count) = capability.config_max_count else {
return Err(Error::InvalidArgs(format!(
"Configuration field '{}' must have 'max_count' set",
name
)));
};
let nested_type = match element {
ConfigNestedValueType::Bool { .. } => cm_rust::ConfigNestedValueType::Bool,
ConfigNestedValueType::Uint8 { .. } => cm_rust::ConfigNestedValueType::Uint8,
ConfigNestedValueType::Uint16 { .. } => cm_rust::ConfigNestedValueType::Uint16,
ConfigNestedValueType::Uint32 { .. } => cm_rust::ConfigNestedValueType::Uint32,
ConfigNestedValueType::Uint64 { .. } => cm_rust::ConfigNestedValueType::Uint64,
ConfigNestedValueType::Int8 { .. } => cm_rust::ConfigNestedValueType::Int8,
ConfigNestedValueType::Int16 { .. } => cm_rust::ConfigNestedValueType::Int16,
ConfigNestedValueType::Int32 { .. } => cm_rust::ConfigNestedValueType::Int32,
ConfigNestedValueType::Int64 { .. } => cm_rust::ConfigNestedValueType::Int64,
ConfigNestedValueType::String { max_size } => {
cm_rust::ConfigNestedValueType::String { max_size: (*max_size).into() }
}
};
cm_rust::ConfigValueType::Vector { max_count: max_count.into(), nested_type }
}
};
let value = config_value_file::field::config_value_from_json_value(value, &config_type)
.map_err(|e| Error::InvalidArgs(format!("Error parsing config '{}': {}", name, e)))?;
Ok(value.native_into_fidl())
}
#[cfg(test)]
pub mod test_util {
macro_rules! must_parse_cml {
($($input:tt)+) => {
serde_json::from_str::<Document>(&json!($($input)+).to_string())
.expect("deserialization failed")
};
}
pub(crate) use must_parse_cml;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::Error;
use crate::features::Feature;
use crate::translate::test_util::must_parse_cml;
use crate::{
create_offer, AnyRef, AsClause, Capability, CapabilityClause, Child, Collection,
DebugRegistration, Document, Environment, EnvironmentExtends, EnvironmentRef, Expose,
ExposeFromRef, ExposeToRef, FromClause, Offer, OfferFromRef, OneOrMany, Path, PathClause,
Program, ResolverRegistration, RightsClause, RunnerRegistration, Use, UseFromRef,
};
use assert_matches::assert_matches;
use cm_fidl_validator::error::{AvailabilityList, DeclField, Error as CmFidlError, ErrorList};
use cm_types::{self as cm, Name};
use difference::Changeset;
use serde_json::{json, Map, Value};
use std::collections::BTreeSet;
use std::convert::Into;
use std::str::FromStr;
use {
fidl_fuchsia_component_decl as fdecl, fidl_fuchsia_data as fdata, fidl_fuchsia_io as fio,
};
macro_rules! test_compile {
(
$(
$(#[$m:meta])*
$test_name:ident => {
$(features = $features:expr,)?
input = $input:expr,
output = $expected:expr,
},
)+
) => {
$(
$(#[$m])*
#[test]
fn $test_name() {
let input = serde_json::from_str(&$input.to_string()).expect("deserialization failed");
let options = CompileOptions::new().config_package_path("fake.cvf");
$(let features = $features; let options = options.features(&features);)?
let actual = compile(&input, options).expect("compilation failed");
if actual != $expected {
let e = format!("{:#?}", $expected);
let a = format!("{:#?}", actual);
panic!("{}", Changeset::new(&a, &e, "\n"));
}
}
)+
};
}
fn default_component_decl() -> fdecl::Component {
fdecl::Component::default()
}
test_compile! {
test_compile_empty => {
input = json!({}),
output = default_component_decl(),
},
test_compile_empty_includes => {
input = json!({ "include": [] }),
output = default_component_decl(),
},
test_compile_offer_to_all_and_diff_sources => {
input = json!({
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
},
],
"collections": [
{
"name": "coll",
"durability": "transient",
},
],
"offer": [
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "all",
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "framework",
"to": "#logger",
"as": "LogSink2",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
target_name: Some("LogSink2".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "coll".into(),
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
]),
children: Some(vec![fdecl::Child {
name: Some("logger".into()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
}]),
collections: Some(vec![fdecl::Collection {
name: Some("coll".into()),
durability: Some(fdecl::Durability::Transient),
..Default::default()
}]),
..default_component_decl()
},
},
test_compile_offer_to_all => {
input = json!({
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
},
{
"name": "something",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
},
],
"collections": [
{
"name": "coll",
"durability": "transient",
},
],
"offer": [
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "all",
},
{
"protocol": "fuchsia.inspect.InspectSink",
"from": "parent",
"to": "all",
},
{
"protocol": "fuchsia.logger.LegacyLog",
"from": "parent",
"to": "#logger",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LegacyLog".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "coll".into(),
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.inspect.InspectSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
target_name: Some("fuchsia.inspect.InspectSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.inspect.InspectSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.inspect.InspectSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.inspect.InspectSink".into()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "coll".into(),
})),
target_name: Some("fuchsia.inspect.InspectSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".into()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
fdecl::Child {
name: Some("something".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
collections: Some(vec![fdecl::Collection {
name: Some("coll".into()),
durability: Some(fdecl::Durability::Transient),
..Default::default()
}]),
..default_component_decl()
},
},
test_compile_offer_to_all_hides_individual_duplicate_routes => {
input = json!({
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
},
{
"name": "something",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
},
{
"name": "something-v2",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
},
],
"collections": [
{
"name": "coll",
"durability": "transient",
},
{
"name": "coll2",
"durability": "transient",
},
],
"offer": [
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "#logger",
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "all",
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": [ "#something", "#something-v2", "#coll2"],
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "#coll",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something-v2".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "coll2".into(),
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "coll".into(),
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".into()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
fdecl::Child {
name: Some("something".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
fdecl::Child {
name: Some("something-v2".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
collections: Some(vec![fdecl::Collection {
name: Some("coll".into()),
durability: Some(fdecl::Durability::Transient),
..Default::default()
}, fdecl::Collection {
name: Some("coll2".into()),
durability: Some(fdecl::Durability::Transient),
..Default::default()
}]),
..default_component_decl()
},
},
test_compile_offer_to_all_from_child => {
input = json!({
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
},
{
"name": "something",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
},
{
"name": "something-v2",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm",
},
],
"offer": [
{
"protocol": "fuchsia.logger.LogSink",
"from": "#logger",
"to": "all",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something-v2".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".into()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".into()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
fdecl::Child {
name: Some("something".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
fdecl::Child {
name: Some("something-v2".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something-v2.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_offer_multiple_protocols_to_single_array_syntax_and_all => {
input = json!({
"children": [
{
"name": "something",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
},
],
"offer": [
{
"protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
"from": "parent",
"to": "#something",
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "all",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.inspect.InspectSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.inspect.InspectSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child {
name: Some("something".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_offer_to_all_array_and_single => {
input = json!({
"children": [
{
"name": "something",
"url": "fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm",
},
],
"offer": [
{
"protocol": ["fuchsia.logger.LogSink", "fuchsia.inspect.InspectSink",],
"from": "parent",
"to": "all",
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "#something",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.logger.LogSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.logger.LogSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Offer::Protocol(fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.inspect.InspectSink".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "something".into(),
collection: None,
})),
target_name: Some("fuchsia.inspect.InspectSink".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child {
name: Some("something".into()),
url: Some(
"fuchsia-pkg://fuchsia.com/something/stable#meta/something.cm".into(),
),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_program => {
input = json!({
"program": {
"runner": "elf",
"binary": "bin/app",
},
}),
output = fdecl::Component {
program: Some(fdecl::Program {
runner: Some("elf".to_string()),
info: Some(fdata::Dictionary {
entries: Some(vec![fdata::DictionaryEntry {
key: "binary".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
}]),
..Default::default()
}),
..Default::default()
}),
..default_component_decl()
},
},
test_compile_program_with_use_runner => {
input = json!({
"program": {
"binary": "bin/app",
},
"use": [
{ "runner": "elf", "from": "parent", },
],
}),
output = fdecl::Component {
program: Some(fdecl::Program {
runner: None,
info: Some(fdata::Dictionary {
entries: Some(vec![fdata::DictionaryEntry {
key: "binary".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
}]),
..Default::default()
}),
..Default::default()
}),
uses: Some(vec![
fdecl::Use::Runner (
fdecl::UseRunner {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("elf".to_string()),
..Default::default()
}
),
]),
..default_component_decl()
},
},
test_compile_program_with_nested_objects => {
input = json!({
"program": {
"runner": "elf",
"binary": "bin/app",
"one": {
"two": {
"three.four": {
"five": "six"
}
},
}
},
}),
output = fdecl::Component {
program: Some(fdecl::Program {
runner: Some("elf".to_string()),
info: 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: "one.two.three.four.five".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("six".to_string()))),
},
]),
..Default::default()
}),
..Default::default()
}),
..default_component_decl()
},
},
test_compile_program_with_array_of_objects => {
input = json!({
"program": {
"runner": "elf",
"binary": "bin/app",
"networks": [
{
"endpoints": [
{
"name": "device",
"mac": "aa:bb:cc:dd:ee:ff"
},
{
"name": "emu",
"mac": "ff:ee:dd:cc:bb:aa"
},
],
"name": "external_network"
}
],
},
}),
output = fdecl::Component {
program: Some(fdecl::Program {
runner: Some("elf".to_string()),
info: 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: "networks".to_string(),
value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
fdata::Dictionary {
entries: Some(vec![
fdata::DictionaryEntry {
key: "endpoints".to_string(),
value: Some(Box::new(fdata::DictionaryValue::ObjVec(vec![
fdata::Dictionary {
entries: Some(vec![
fdata::DictionaryEntry {
key: "mac".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("aa:bb:cc:dd:ee:ff".to_string()))),
},
fdata::DictionaryEntry {
key: "name".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("device".to_string()))),
}
]),
..Default::default()
},
fdata::Dictionary {
entries: Some(vec![
fdata::DictionaryEntry {
key: "mac".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("ff:ee:dd:cc:bb:aa".to_string()))),
},
fdata::DictionaryEntry {
key: "name".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("emu".to_string()))),
}
]),
..Default::default()
},
])))
},
fdata::DictionaryEntry {
key: "name".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("external_network".to_string()))),
},
]),
..Default::default()
}
]))),
},
]),
..Default::default()
}),
..Default::default()
}),
..default_component_decl()
},
},
test_compile_use => {
input = json!({
"use": [
{
"protocol": "LegacyCoolFonts",
"path": "/svc/fuchsia.fonts.LegacyProvider",
"availability": "optional",
},
{ "protocol": "fuchsia.sys2.LegacyRealm", "from": "framework" },
{ "protocol": "fuchsia.sys2.StorageAdmin", "from": "#data-storage" },
{ "protocol": "fuchsia.sys2.DebugProto", "from": "debug" },
{ "protocol": "fuchsia.sys2.DictionaryProto", "from": "#logger/in/dict" },
{ "protocol": "fuchsia.sys2.Echo", "from": "self", "availability": "transitional" },
{ "service": "fuchsia.sys2.EchoService", "from": "parent/dict", },
{ "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" },
{
"event_stream": "bar_stream",
},
{
"event_stream": ["foobar", "stream"],
"scope": ["#logger", "#modular"],
"path": "/event_stream/another",
},
{ "runner": "usain", "from": "parent", },
],
"capabilities": [
{ "protocol": "fuchsia.sys2.Echo" },
{
"config": "fuchsia.config.Config",
"type": "bool",
"value": true,
},
{
"storage": "data-storage",
"from": "parent",
"backing_dir": "minfs",
"storage_id": "static_instance_id_or_moniker",
}
],
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
"environment": "#env_one"
}
],
"collections": [
{
"name": "modular",
"durability": "transient",
},
],
"environments": [
{
"name": "env_one",
"extends": "realm",
}
]
}),
output = fdecl::Component {
uses: Some(vec![
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("LegacyCoolFonts".to_string()),
target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
source_name: Some("fuchsia.sys2.LegacyRealm".to_string()),
target_path: Some("/svc/fuchsia.sys2.LegacyRealm".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef { name: "data-storage".to_string() })),
source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
target_path: Some("/svc/fuchsia.sys2.StorageAdmin".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
source_name: Some("fuchsia.sys2.DebugProto".to_string()),
target_path: Some("/svc/fuchsia.sys2.DebugProto".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("in/dict".into()),
source_name: Some("fuchsia.sys2.DictionaryProto".to_string()),
target_path: Some("/svc/fuchsia.sys2.DictionaryProto".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("fuchsia.sys2.Echo".to_string()),
target_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
availability: Some(fdecl::Availability::Transitional),
..Default::default()
}
),
fdecl::Use::Service (
fdecl::UseService {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("dict".into()),
source_name: Some("fuchsia.sys2.EchoService".to_string()),
target_path: Some("/svc/fuchsia.sys2.EchoService".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Directory (
fdecl::UseDirectory {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("assets".to_string()),
target_path: Some("/data/assets".to_string()),
rights: Some(fio::Operations::READ_BYTES),
subdir: None,
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Directory (
fdecl::UseDirectory {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("config".to_string()),
target_path: Some("/data/config".to_string()),
rights: Some(fio::Operations::READ_BYTES),
subdir: Some("fonts".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Storage (
fdecl::UseStorage {
source_name: Some("hippos".to_string()),
target_path: Some("/hippos".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Storage (
fdecl::UseStorage {
source_name: Some("cache".to_string()),
target_path: Some("/tmp".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::EventStream(fdecl::UseEventStream {
source_name: Some("bar_stream".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
target_path: Some("/svc/fuchsia.component.EventStream".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Use::EventStream(fdecl::UseEventStream {
source_name: Some("foobar".to_string()),
scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
target_path: Some("/event_stream/another".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Use::EventStream(fdecl::UseEventStream {
source_name: Some("stream".to_string()),
scope: Some(vec![fdecl::Ref::Child(fdecl::ChildRef{name:"logger".to_string(), collection: None}), fdecl::Ref::Collection(fdecl::CollectionRef{name:"modular".to_string()})]),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
target_path: Some("/event_stream/another".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}),
fdecl::Use::Runner(fdecl::UseRunner {
source_name: Some("usain".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef{})),
..Default::default()
}),
]),
collections: Some(vec![
fdecl::Collection{
name:Some("modular".to_string()),
durability:Some(fdecl::Durability::Transient),
..Default::default()
},
]),
capabilities: Some(vec![
fdecl::Capability::Protocol(fdecl::Protocol {
name: Some("fuchsia.sys2.Echo".to_string()),
source_path: Some("/svc/fuchsia.sys2.Echo".to_string()),
..Default::default()
}),
fdecl::Capability::Config(fdecl::Configuration {
name: Some("fuchsia.config.Config".to_string()),
value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
..Default::default()
}),
fdecl::Capability::Storage(fdecl::Storage {
name: Some("data-storage".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
backing_dir: Some("minfs".to_string()),
subdir: None,
storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child{
name:Some("logger".to_string()),
url:Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup:Some(fdecl::StartupMode::Lazy),
environment: Some("env_one".to_string()),
..Default::default()
}
]),
environments: Some(vec![
fdecl::Environment {
name: Some("env_one".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
..Default::default()
},
]),
config: None,
..default_component_decl()
},
},
test_compile_expose => {
input = json!({
"expose": [
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog",
"to": "parent"
},
{
"protocol": [ "A", "B" ],
"from": "self",
"to": "parent"
},
{
"protocol": "C",
"from": "#data-storage",
},
{
"protocol": "D",
"from": "#logger/in/dict",
"as": "E",
},
{
"service": "F",
"from": "#logger/in/dict",
},
{
"service": "svc",
"from": [ "#logger", "#coll", "self" ],
},
{
"directory": "blob",
"from": "self",
"to": "framework",
"rights": ["r*"],
},
{
"directory": [ "blob2", "blob3" ],
"from": "#logger",
"to": "parent",
},
{ "directory": "hub", "from": "framework" },
{ "runner": "web", "from": "#logger", "to": "parent", "as": "web-rename" },
{ "runner": [ "runner_a", "runner_b" ], "from": "#logger" },
{ "resolver": "my_resolver", "from": "#logger", "to": "parent", "as": "pkg_resolver" },
{ "resolver": [ "resolver_a", "resolver_b" ], "from": "#logger" },
{ "dictionary": [ "dictionary_a", "dictionary_b" ], "from": "#logger" },
],
"capabilities": [
{ "protocol": "A" },
{ "protocol": "B" },
{ "service": "svc" },
{
"directory": "blob",
"path": "/volumes/blobfs/blob",
"rights": ["r*"],
},
{
"runner": "web",
"path": "/svc/fuchsia.component.ComponentRunner",
},
{
"storage": "data-storage",
"from": "parent",
"backing_dir": "minfs",
"storage_id": "static_instance_id_or_moniker",
},
],
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
},
],
"collections": [
{
"name": "coll",
"durability": "transient",
},
],
}),
output = fdecl::Component {
exposes: Some(vec![
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("A".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("A".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("B".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("B".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
name: "data-storage".to_string(),
})),
source_name: Some("C".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("C".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("in/dict".into()),
source_name: Some("D".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("E".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Service (
fdecl::ExposeService {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
source_name: Some("F".into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("in/dict".into()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("F".into()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Service (
fdecl::ExposeService {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Service (
fdecl::ExposeService {
source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "coll".into(),
})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Service (
fdecl::ExposeService {
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Directory (
fdecl::ExposeDirectory {
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("blob".to_string()),
target: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
target_name: Some("blob".to_string()),
rights: Some(
fio::Operations::CONNECT | fio::Operations::ENUMERATE |
fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
fio::Operations::GET_ATTRIBUTES
),
subdir: None,
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Directory (
fdecl::ExposeDirectory {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("blob2".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("blob2".to_string()),
rights: None,
subdir: None,
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Directory (
fdecl::ExposeDirectory {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("blob3".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("blob3".to_string()),
rights: None,
subdir: None,
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Directory (
fdecl::ExposeDirectory {
source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
source_name: Some("hub".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("hub".to_string()),
rights: None,
subdir: None,
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Runner (
fdecl::ExposeRunner {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("web".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("web-rename".to_string()),
..Default::default()
}
),
fdecl::Expose::Runner (
fdecl::ExposeRunner {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("runner_a".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("runner_a".to_string()),
..Default::default()
}
),
fdecl::Expose::Runner (
fdecl::ExposeRunner {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("runner_b".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("runner_b".to_string()),
..Default::default()
}
),
fdecl::Expose::Resolver (
fdecl::ExposeResolver {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("my_resolver".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("pkg_resolver".to_string()),
..Default::default()
}
),
fdecl::Expose::Resolver (
fdecl::ExposeResolver {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("resolver_a".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("resolver_a".to_string()),
..Default::default()
}
),
fdecl::Expose::Resolver (
fdecl::ExposeResolver {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("resolver_b".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("resolver_b".to_string()),
..Default::default()
}
),
fdecl::Expose::Dictionary (
fdecl::ExposeDictionary {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("dictionary_a".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("dictionary_a".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Dictionary (
fdecl::ExposeDictionary {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("dictionary_b".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("dictionary_b".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
]),
offers: None,
capabilities: Some(vec![
fdecl::Capability::Protocol (
fdecl::Protocol {
name: Some("A".to_string()),
source_path: Some("/svc/A".to_string()),
..Default::default()
}
),
fdecl::Capability::Protocol (
fdecl::Protocol {
name: Some("B".to_string()),
source_path: Some("/svc/B".to_string()),
..Default::default()
}
),
fdecl::Capability::Service (
fdecl::Service {
name: Some("svc".to_string()),
source_path: Some("/svc/svc".to_string()),
..Default::default()
}
),
fdecl::Capability::Directory (
fdecl::Directory {
name: Some("blob".to_string()),
source_path: Some("/volumes/blobfs/blob".to_string()),
rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
fio::Operations::GET_ATTRIBUTES
),
..Default::default()
}
),
fdecl::Capability::Runner (
fdecl::Runner {
name: Some("web".to_string()),
source_path: Some("/svc/fuchsia.component.ComponentRunner".to_string()),
..Default::default()
}
),
fdecl::Capability::Storage(fdecl::Storage {
name: Some("data-storage".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
backing_dir: Some("minfs".to_string()),
subdir: None,
storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
..Default::default()
}),
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
}
]),
collections: Some(vec![
fdecl::Collection {
name: Some("coll".to_string()),
durability: Some(fdecl::Durability::Transient),
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_expose_other_availability => {
input = json!({
"expose": [
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_default",
"to": "parent"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_required",
"to": "parent",
"availability": "required"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_optional",
"to": "parent",
"availability": "optional"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_same_as_target",
"to": "parent",
"availability": "same_as_target"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_transitional",
"to": "parent",
"availability": "transitional"
},
],
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
},
],
}),
output = fdecl::Component {
exposes: Some(vec![
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_default".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_required".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_optional".to_string()),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_same_as_target".to_string()),
availability: Some(fdecl::Availability::SameAsTarget),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_transitional".to_string()),
availability: Some(fdecl::Availability::Transitional),
..Default::default()
}
),
]),
offers: None,
capabilities: None,
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_expose_source_availability_unknown => {
input = json!({
"expose": [
{
"protocol": "fuchsia.logger.Log",
"from": "#non-existent",
"as": "fuchsia.logger.LegacyLog_non_existent",
"availability": "optional",
"source_availability": "unknown"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#non-existent/dict",
"as": "fuchsia.logger.LegacyLog_non_existent2",
"availability": "optional",
"source_availability": "unknown"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_child_exist",
"availability": "optional",
"source_availability": "unknown"
},
],
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
},
],
}),
output = fdecl::Component {
exposes: Some(vec![
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Expose::Protocol (
fdecl::ExposeProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_offer_source_availability_unknown => {
input = json!({
"offer": [
{
"protocol": "fuchsia.logger.Log",
"from": "#non-existent",
"as": "fuchsia.logger.LegacyLog_non_existent",
"to": "#target",
"availability": "optional",
"source_availability": "unknown"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#non-existent/dict",
"as": "fuchsia.logger.LegacyLog_non_existent2",
"to": "#target",
"availability": "optional",
"source_availability": "unknown"
},
{
"protocol": "fuchsia.logger.Log",
"from": "#logger",
"as": "fuchsia.logger.LegacyLog_child_exist",
"to": "#target",
"availability": "optional",
"source_availability": "unknown"
},
],
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm"
},
{
"name": "target",
"url": "#meta/target.cm"
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "target".to_string(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog_non_existent".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::VoidType(fdecl::VoidRef { })),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "target".to_string(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog_non_existent2".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.Log".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "target".to_string(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog_child_exist".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
fdecl::Child {
name: Some("target".to_string()),
url: Some("#meta/target.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_offer => {
input = json!({
"offer": [
{
"protocol": "fuchsia.logger.LegacyLog",
"from": "#logger",
"to": "#netstack", "dependency": "weak"
},
{
"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" ],
"availability": "optional"
},
{
"protocol": "fuchsia.sys2.StorageAdmin",
"from": "#data",
"to": [ "#modular" ],
},
{
"protocol": "fuchsia.sys2.FromDict",
"from": "parent/in/dict",
"to": [ "#modular" ],
},
{
"service": "svc",
"from": [ "parent", "self", "#logger", "#modular" ],
"to": "#netstack",
},
{
"service": "fuchsia.sys2.FromDictService",
"from": [ "parent/in/dict"],
"to": "#modular",
"dependency": "weak",
},
{
"directory": "assets",
"from": "parent",
"to": [ "#netstack" ],
"dependency": "weak",
"availability": "same_as_target"
},
{
"directory": [ "assets2", "assets3" ],
"from": "parent",
"to": [ "#modular", "#netstack" ],
},
{
"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"
],
},
{
"storage": [ "storage_a", "storage_b" ],
"from": "parent",
"to": "#netstack",
},
{
"runner": "elf",
"from": "parent",
"to": [ "#modular" ],
"as": "elf-renamed",
},
{
"runner": [ "runner_a", "runner_b" ],
"from": "parent",
"to": "#netstack",
},
{
"resolver": "my_resolver",
"from": "parent",
"to": [ "#modular" ],
"as": "pkg_resolver",
},
{
"resolver": [ "resolver_a", "resolver_b" ],
"from": "parent",
"to": "#netstack",
},
{
"dictionary": [ "dictionary_a", "dictionary_b" ],
"from": "parent",
"to": "#netstack",
},
{
"event_stream": [
"running",
"started",
],
"from": "parent",
"to": "#netstack",
},
{
"event_stream": "stopped",
"from": "parent",
"to": "#netstack",
"as": "some_other_event",
},
],
"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": "transient",
},
],
"capabilities": [
{
"service": "svc",
},
{
"storage": "data",
"backing_dir": "minfs",
"from": "#logger",
"storage_id": "static_instance_id_or_moniker",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.LegacyLog".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog".to_string()),
dependency_type: Some(fdecl::DependencyType::Weak),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.LegacyLog".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("fuchsia.logger.LegacySysLog".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.setui.SetUiService".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("fuchsia.setui.SetUiService".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.test.service.Name".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("fuchsia.test.service.Name".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Optional),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
name: "data".to_string(),
})),
source_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("fuchsia.sys2.StorageAdmin".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("in/dict".into()),
source_name: Some("fuchsia.sys2.FromDict".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("fuchsia.sys2.FromDict".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Service (
fdecl::OfferService {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".into(),
collection: None,
})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
dependency_type: Some(fdecl::DependencyType::Strong),
..Default::default()
}
),
fdecl::Offer::Service (
fdecl::OfferService {
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".into(),
collection: None,
})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
dependency_type: Some(fdecl::DependencyType::Strong),
..Default::default()
}
),
fdecl::Offer::Service (
fdecl::OfferService {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".into(),
collection: None,
})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".into(),
collection: None,
})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
dependency_type: Some(fdecl::DependencyType::Strong),
..Default::default()
}
),
fdecl::Offer::Service (
fdecl::OfferService {
source: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".into(),
})),
source_name: Some("svc".into()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".into(),
collection: None,
})),
target_name: Some("svc".into()),
availability: Some(fdecl::Availability::Required),
dependency_type: Some(fdecl::DependencyType::Strong),
..Default::default()
}
),
fdecl::Offer::Service (
fdecl::OfferService {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("fuchsia.sys2.FromDictService".into()),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("in/dict".into()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".into(),
})),
target_name: Some("fuchsia.sys2.FromDictService".to_string()),
availability: Some(fdecl::Availability::Required),
dependency_type: Some(fdecl::DependencyType::Weak),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("assets".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("assets".to_string()),
rights: None,
subdir: None,
dependency_type: Some(fdecl::DependencyType::Weak),
availability: Some(fdecl::Availability::SameAsTarget),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("assets2".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("assets2".to_string()),
rights: None,
subdir: None,
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("assets3".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("assets3".to_string()),
rights: None,
subdir: None,
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("assets2".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("assets2".to_string()),
rights: None,
subdir: None,
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("assets3".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("assets3".to_string()),
rights: None,
subdir: None,
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("data".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("assets".to_string()),
rights: None,
subdir: Some("index/file".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Directory (
fdecl::OfferDirectory {
source: Some(fdecl::Ref::Framework(fdecl::FrameworkRef {})),
source_name: Some("hub".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("hub".to_string()),
rights: None,
subdir: None,
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Storage (
fdecl::OfferStorage {
source_name: Some("data".to_string()),
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("data".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Storage (
fdecl::OfferStorage {
source_name: Some("data".to_string()),
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("data".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Storage (
fdecl::OfferStorage {
source_name: Some("storage_a".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("storage_a".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Storage (
fdecl::OfferStorage {
source_name: Some("storage_b".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("storage_b".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Runner (
fdecl::OfferRunner {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("elf".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("elf-renamed".to_string()),
..Default::default()
}
),
fdecl::Offer::Runner (
fdecl::OfferRunner {
source_name: Some("runner_a".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("runner_a".to_string()),
..Default::default()
}
),
fdecl::Offer::Runner (
fdecl::OfferRunner {
source_name: Some("runner_b".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("runner_b".to_string()),
..Default::default()
}
),
fdecl::Offer::Resolver (
fdecl::OfferResolver {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("my_resolver".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("pkg_resolver".to_string()),
..Default::default()
}
),
fdecl::Offer::Resolver (
fdecl::OfferResolver {
source_name: Some("resolver_a".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("resolver_a".to_string()),
..Default::default()
}
),
fdecl::Offer::Resolver (
fdecl::OfferResolver {
source_name: Some("resolver_b".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("resolver_b".to_string()),
..Default::default()
}
),
fdecl::Offer::Dictionary (
fdecl::OfferDictionary {
source_name: Some("dictionary_a".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("dictionary_a".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Dictionary (
fdecl::OfferDictionary {
source_name: Some("dictionary_b".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("dictionary_b".to_string()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::EventStream (
fdecl::OfferEventStream {
source_name: Some("running".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("running".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::EventStream (
fdecl::OfferEventStream {
source_name: Some("started".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("started".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::EventStream (
fdecl::OfferEventStream {
source_name: Some("stopped".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("some_other_event".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
]),
capabilities: Some(vec![
fdecl::Capability::Service (
fdecl::Service {
name: Some("svc".into()),
source_path: Some("/svc/svc".into()),
..Default::default()
},
),
fdecl::Capability::Storage (
fdecl::Storage {
name: Some("data".to_string()),
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
backing_dir: Some("minfs".to_string()),
subdir: None,
storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
..Default::default()
}
)
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
},
fdecl::Child {
name: Some("netstack".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
},
]),
collections: Some(vec![
fdecl::Collection {
name: Some("modular".to_string()),
durability: Some(fdecl::Durability::Transient),
environment: None,
allowed_offers: None,
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_offer_route_to_dictionary => {
input = json!({
"offer": [
{
"protocol": "A",
"from": "parent/dict/1",
"to": "self/dict",
},
{
"runner": "B",
"from": "#child",
"to": "self/dict",
},
{
"config": "B",
"from": "parent/dict/2",
"to": "self/dict",
"as": "C",
},
],
"children": [
{
"name": "child",
"url": "fuchsia-pkg://child"
},
],
"capabilities": [
{
"dictionary": "dict",
},
],
}),
output = fdecl::Component {
offers: Some(vec![
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("dict/1".into()),
source_name: Some("A".into()),
target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
name: "dict".to_string(),
})),
target_name: Some("A".into()),
dependency_type: Some(fdecl::DependencyType::Strong),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Runner (
fdecl::OfferRunner {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "child".into(),
collection: None,
})),
source_name: Some("B".into()),
target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
name: "dict".to_string(),
})),
target_name: Some("B".into()),
..Default::default()
}
),
fdecl::Offer::Config (
fdecl::OfferConfiguration {
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
#[cfg(fuchsia_api_level_at_least = "25")]
source_dictionary: Some("dict/2".into()),
source_name: Some("B".into()),
target: Some(fdecl::Ref::Capability(fdecl::CapabilityRef {
name: "dict".to_string(),
})),
target_name: Some("C".into()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
]),
capabilities: Some(vec![
fdecl::Capability::Dictionary (
fdecl::Dictionary {
name: Some("dict".into()),
..Default::default()
}
)
]),
children: Some(vec![
fdecl::Child {
name: Some("child".to_string()),
url: Some("fuchsia-pkg://child".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
..Default::default()
},
]),
..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",
"on_terminate": "reboot",
"environment": "#myenv",
},
],
"environments": [
{
"name": "myenv",
"extends": "realm",
},
],
}),
output = fdecl::Component {
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
},
fdecl::Child {
name: Some("gmail".to_string()),
url: Some("https://www.google.com/gmail".to_string()),
startup: Some(fdecl::StartupMode::Eager),
environment: None,
on_terminate: None,
..Default::default()
},
fdecl::Child {
name: Some("echo".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/echo/stable#meta/echo.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: Some("myenv".to_string()),
on_terminate: Some(fdecl::OnTerminate::Reboot),
..Default::default()
}
]),
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
runners: None,
resolvers: None,
stop_timeout_ms: None,
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_collections => {
input = json!({
"collections": [
{
"name": "modular",
"durability": "single_run",
},
{
"name": "tests",
"durability": "transient",
"environment": "#myenv",
},
],
"environments": [
{
"name": "myenv",
"extends": "realm",
}
],
}),
output = fdecl::Component {
collections: Some(vec![
fdecl::Collection {
name: Some("modular".to_string()),
durability: Some(fdecl::Durability::SingleRun),
environment: None,
allowed_offers: None,
..Default::default()
},
fdecl::Collection {
name: Some("tests".to_string()),
durability: Some(fdecl::Durability::Transient),
environment: Some("myenv".to_string()),
allowed_offers: None,
..Default::default()
}
]),
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
runners: None,
resolvers: None,
stop_timeout_ms: None,
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_capabilities => {
features = FeatureSet::from(vec![Feature::DynamicDictionaries]),
input = json!({
"capabilities": [
{
"protocol": "myprotocol",
"path": "/protocol",
},
{
"protocol": "myprotocol2",
},
{
"protocol": [ "myprotocol3", "myprotocol4" ],
},
{
"directory": "mydirectory",
"path": "/directory",
"rights": [ "connect" ],
},
{
"storage": "mystorage",
"backing_dir": "storage",
"from": "#minfs",
"storage_id": "static_instance_id_or_moniker",
},
{
"storage": "mystorage2",
"backing_dir": "storage2",
"from": "#minfs",
"storage_id": "static_instance_id",
},
{
"runner": "myrunner",
"path": "/runner",
},
{
"resolver": "myresolver",
"path": "/resolver"
},
{
"dictionary": "dict1",
},
{
"dictionary": "dict2",
"path": "/in/a",
},
],
"children": [
{
"name": "minfs",
"url": "fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm",
},
]
}),
output = fdecl::Component {
capabilities: Some(vec![
fdecl::Capability::Protocol (
fdecl::Protocol {
name: Some("myprotocol".to_string()),
source_path: Some("/protocol".to_string()),
..Default::default()
}
),
fdecl::Capability::Protocol (
fdecl::Protocol {
name: Some("myprotocol2".to_string()),
source_path: Some("/svc/myprotocol2".to_string()),
..Default::default()
}
),
fdecl::Capability::Protocol (
fdecl::Protocol {
name: Some("myprotocol3".to_string()),
source_path: Some("/svc/myprotocol3".to_string()),
..Default::default()
}
),
fdecl::Capability::Protocol (
fdecl::Protocol {
name: Some("myprotocol4".to_string()),
source_path: Some("/svc/myprotocol4".to_string()),
..Default::default()
}
),
fdecl::Capability::Directory (
fdecl::Directory {
name: Some("mydirectory".to_string()),
source_path: Some("/directory".to_string()),
rights: Some(fio::Operations::CONNECT),
..Default::default()
}
),
fdecl::Capability::Storage (
fdecl::Storage {
name: Some("mystorage".to_string()),
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "minfs".to_string(),
collection: None,
})),
backing_dir: Some("storage".to_string()),
subdir: None,
storage_id: Some(fdecl::StorageId::StaticInstanceIdOrMoniker),
..Default::default()
}
),
fdecl::Capability::Storage (
fdecl::Storage {
name: Some("mystorage2".to_string()),
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "minfs".to_string(),
collection: None,
})),
backing_dir: Some("storage2".to_string()),
subdir: None,
storage_id: Some(fdecl::StorageId::StaticInstanceId),
..Default::default()
}
),
fdecl::Capability::Runner (
fdecl::Runner {
name: Some("myrunner".to_string()),
source_path: Some("/runner".to_string()),
..Default::default()
}
),
fdecl::Capability::Resolver (
fdecl::Resolver {
name: Some("myresolver".to_string()),
source_path: Some("/resolver".to_string()),
..Default::default()
}
),
fdecl::Capability::Dictionary (
fdecl::Dictionary {
name: Some("dict1".into()),
..Default::default()
}
),
fdecl::Capability::Dictionary (
fdecl::Dictionary {
name: Some("dict2".into()),
source: None,
source_dictionary: None,
source_path: Some("/in/a".into()),
..Default::default()
}
),
]),
children: Some(vec![
fdecl::Child {
name: Some("minfs".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/minfs/stable#meta/minfs.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
}
]),
..default_component_decl()
},
},
test_compile_facets => {
input = json!({
"facets": {
"title": "foo",
"authors": [ "me", "you" ],
"year": "2018",
"metadata": {
"publisher": "The Books Publisher",
}
}
}),
output = fdecl::Component {
facets: Some(fdata::Dictionary {
entries: Some(vec![
fdata::DictionaryEntry {
key: "authors".to_string(),
value: Some(Box::new(fdata::DictionaryValue::StrVec(vec!["me".to_owned(), "you".to_owned()]))),
},
fdata::DictionaryEntry {
key: "metadata.publisher".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("The Books Publisher".to_string()))),
},
fdata::DictionaryEntry {
key: "title".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("foo".to_string()))),
},
fdata::DictionaryEntry {
key: "year".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
},
]),
..Default::default()
}
),
..default_component_decl()
},
},
test_compile_environment => {
input = json!({
"environments": [
{
"name": "myenv",
"__stop_timeout_ms": 10u32,
},
{
"name": "myenv2",
"extends": "realm",
},
{
"name": "myenv3",
"extends": "none",
"__stop_timeout_ms": 8000u32,
}
],
}),
output = fdecl::Component {
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::None),
runners: None,
resolvers: None,
stop_timeout_ms: Some(10),
..Default::default()
},
fdecl::Environment {
name: Some("myenv2".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
runners: None,
resolvers: None,
stop_timeout_ms: None,
..Default::default()
},
fdecl::Environment {
name: Some("myenv3".to_string()),
extends: Some(fdecl::EnvironmentExtends::None),
runners: None,
resolvers: None,
stop_timeout_ms: Some(8000),
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_environment_with_runner_and_resolver => {
input = json!({
"environments": [
{
"name": "myenv",
"extends": "realm",
"runners": [
{
"runner": "dart",
"from": "parent",
}
],
"resolvers": [
{
"resolver": "pkg_resolver",
"from": "parent",
"scheme": "fuchsia-pkg",
}
],
},
],
}),
output = fdecl::Component {
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
runners: Some(vec![
fdecl::RunnerRegistration {
source_name: Some("dart".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("dart".to_string()),
..Default::default()
}
]),
resolvers: Some(vec![
fdecl::ResolverRegistration {
resolver: Some("pkg_resolver".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
scheme: Some("fuchsia-pkg".to_string()),
..Default::default()
}
]),
stop_timeout_ms: None,
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_environment_with_runner_alias => {
input = json!({
"environments": [
{
"name": "myenv",
"extends": "realm",
"runners": [
{
"runner": "dart",
"from": "parent",
"as": "my-dart",
}
],
},
],
}),
output = fdecl::Component {
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
runners: Some(vec![
fdecl::RunnerRegistration {
source_name: Some("dart".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("my-dart".to_string()),
..Default::default()
}
]),
resolvers: None,
stop_timeout_ms: None,
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_environment_with_debug => {
input = json!({
"capabilities": [
{
"protocol": "fuchsia.serve.service",
},
],
"environments": [
{
"name": "myenv",
"extends": "realm",
"debug": [
{
"protocol": "fuchsia.serve.service",
"from": "self",
"as": "my-service",
}
],
},
],
}),
output = fdecl::Component {
capabilities: Some(vec![
fdecl::Capability::Protocol(
fdecl::Protocol {
name : Some("fuchsia.serve.service".to_owned()),
source_path: Some("/svc/fuchsia.serve.service".to_owned()),
..Default::default()
}
)
]),
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
debug_capabilities: Some(vec![
fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
source_name: Some("fuchsia.serve.service".to_string()),
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
target_name: Some("my-service".to_string()),
..Default::default()
}),
]),
resolvers: None,
runners: None,
stop_timeout_ms: None,
..Default::default()
},
]),
..default_component_decl()
},
},
test_compile_configuration_capability => {
input = json!({
"capabilities": [
{
"config": "fuchsia.config.true",
"type": "bool",
"value": true,
},
{
"config": "fuchsia.config.false",
"type": "bool",
"value": false,
},
],
}),
output = fdecl::Component {
capabilities: Some(vec![
fdecl::Capability::Config (
fdecl::Configuration {
name: Some("fuchsia.config.true".to_string()),
value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
..Default::default()
}),
fdecl::Capability::Config (
fdecl::Configuration {
name: Some("fuchsia.config.false".to_string()),
value: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(false))),
..Default::default()
}),
]),
..default_component_decl()
},
},
test_compile_all_sections => {
input = json!({
"program": {
"runner": "elf",
"binary": "bin/app",
},
"use": [
{ "protocol": "LegacyCoolFonts", "path": "/svc/fuchsia.fonts.LegacyProvider" },
{ "protocol": [ "ReallyGoodFonts", "IWouldNeverUseTheseFonts"]},
{ "protocol": "DebugProtocol", "from": "debug"},
],
"expose": [
{ "directory": "blobfs", "from": "self", "rights": ["r*"]},
],
"offer": [
{
"protocol": "fuchsia.logger.LegacyLog",
"from": "#logger",
"to": [ "#netstack", "#modular" ],
"dependency": "weak"
},
],
"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": "transient",
},
],
"capabilities": [
{
"directory": "blobfs",
"path": "/volumes/blobfs",
"rights": [ "r*" ],
},
{
"runner": "myrunner",
"path": "/runner",
},
{
"protocol": "fuchsia.serve.service",
}
],
"facets": {
"author": "Fuchsia",
"year": "2018",
},
"environments": [
{
"name": "myenv",
"extends": "realm",
"debug": [
{
"protocol": "fuchsia.serve.service",
"from": "self",
"as": "my-service",
},
{
"protocol": "fuchsia.logger.LegacyLog",
"from": "#logger",
}
]
}
],
}),
output = fdecl::Component {
program: Some(fdecl::Program {
runner: Some("elf".to_string()),
info: Some(fdata::Dictionary {
entries: Some(vec![fdata::DictionaryEntry {
key: "binary".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("bin/app".to_string()))),
}]),
..Default::default()
}),
..Default::default()
}),
uses: Some(vec![
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("LegacyCoolFonts".to_string()),
target_path: Some("/svc/fuchsia.fonts.LegacyProvider".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("ReallyGoodFonts".to_string()),
target_path: Some("/svc/ReallyGoodFonts".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
source_name: Some("IWouldNeverUseTheseFonts".to_string()),
target_path: Some("/svc/IWouldNeverUseTheseFonts".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Use::Protocol (
fdecl::UseProtocol {
dependency_type: Some(fdecl::DependencyType::Strong),
source: Some(fdecl::Ref::Debug(fdecl::DebugRef {})),
source_name: Some("DebugProtocol".to_string()),
target_path: Some("/svc/DebugProtocol".to_string()),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
]),
exposes: Some(vec![
fdecl::Expose::Directory (
fdecl::ExposeDirectory {
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
source_name: Some("blobfs".to_string()),
target: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("blobfs".to_string()),
rights: Some(
fio::Operations::CONNECT | fio::Operations::ENUMERATE |
fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
fio::Operations::GET_ATTRIBUTES
),
subdir: None,
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
]),
offers: Some(vec![
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.LegacyLog".to_string()),
target: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "netstack".to_string(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog".to_string()),
dependency_type: Some(fdecl::DependencyType::Weak),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
fdecl::Offer::Protocol (
fdecl::OfferProtocol {
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
source_name: Some("fuchsia.logger.LegacyLog".to_string()),
target: Some(fdecl::Ref::Collection(fdecl::CollectionRef {
name: "modular".to_string(),
})),
target_name: Some("fuchsia.logger.LegacyLog".to_string()),
dependency_type: Some(fdecl::DependencyType::Weak),
availability: Some(fdecl::Availability::Required),
..Default::default()
}
),
]),
capabilities: Some(vec![
fdecl::Capability::Directory (
fdecl::Directory {
name: Some("blobfs".to_string()),
source_path: Some("/volumes/blobfs".to_string()),
rights: Some(fio::Operations::CONNECT | fio::Operations::ENUMERATE |
fio::Operations::TRAVERSE | fio::Operations::READ_BYTES |
fio::Operations::GET_ATTRIBUTES
),
..Default::default()
}
),
fdecl::Capability::Runner (
fdecl::Runner {
name: Some("myrunner".to_string()),
source_path: Some("/runner".to_string()),
..Default::default()
}
),
fdecl::Capability::Protocol(
fdecl::Protocol {
name : Some("fuchsia.serve.service".to_owned()),
source_path: Some("/svc/fuchsia.serve.service".to_owned()),
..Default::default()
}
)
]),
children: Some(vec![
fdecl::Child {
name: Some("logger".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
},
fdecl::Child {
name: Some("netstack".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/netstack/stable#meta/netstack.cm".to_string()),
startup: Some(fdecl::StartupMode::Lazy),
environment: None,
on_terminate: None,
..Default::default()
},
]),
collections: Some(vec![
fdecl::Collection {
name: Some("modular".to_string()),
durability: Some(fdecl::Durability::Transient),
environment: None,
allowed_offers: None,
..Default::default()
}
]),
environments: Some(vec![
fdecl::Environment {
name: Some("myenv".to_string()),
extends: Some(fdecl::EnvironmentExtends::Realm),
runners: None,
resolvers: None,
stop_timeout_ms: None,
debug_capabilities: Some(vec![
fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
source_name: Some("fuchsia.serve.service".to_string()),
source: Some(fdecl::Ref::Self_(fdecl::SelfRef {})),
target_name: Some("my-service".to_string()),
..Default::default()
}),
fdecl::DebugRegistration::Protocol( fdecl::DebugProtocolRegistration {
source_name: Some("fuchsia.logger.LegacyLog".to_string()),
source: Some(fdecl::Ref::Child(fdecl::ChildRef {
name: "logger".to_string(),
collection: None,
})),
target_name: Some("fuchsia.logger.LegacyLog".to_string()),
..Default::default()
}),
]),
..Default::default()
}
]),
facets: Some(fdata::Dictionary {
entries: Some(vec![
fdata::DictionaryEntry {
key: "author".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("Fuchsia".to_string()))),
},
fdata::DictionaryEntry {
key: "year".to_string(),
value: Some(Box::new(fdata::DictionaryValue::Str("2018".to_string()))),
},
]),
..Default::default()
}),
..Default::default()
},
},
}
#[test]
fn test_maybe_generate_specialization_from_all() {
let offer = create_offer(
"fuchsia.logger.LegacyLog",
OneOrMany::One(OfferFromRef::Parent {}),
OneOrMany::One(OfferToRef::All),
);
let mut offer_set = vec![create_offer(
"fuchsia.logger.LogSink",
OneOrMany::One(OfferFromRef::Parent {}),
OneOrMany::One(OfferToRef::All),
)];
let result = maybe_generate_direct_offer_from_all(
&offer,
&offer_set,
&Name::from_str("something").unwrap(),
);
assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
assert_eq!(
protocol,
&Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
);
assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
assert_eq!(
to,
&OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
);
});
offer_set.push(create_offer(
"fuchsia.inspect.InspectSink",
OneOrMany::One(OfferFromRef::Parent {}),
OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
));
let result = maybe_generate_direct_offer_from_all(
&offer,
&offer_set,
&Name::from_str("something").unwrap(),
);
assert_matches!(&result[..], [Offer {protocol, from, to, ..}] => {
assert_eq!(
protocol,
&Some(OneOrMany::One(Name::from_str("fuchsia.logger.LegacyLog").unwrap())),
);
assert_eq!(from, &OneOrMany::One(OfferFromRef::Parent {}));
assert_eq!(
to,
&OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
);
});
offer_set.push(create_offer(
"fuchsia.logger.LegacyLog",
OneOrMany::One(OfferFromRef::Parent {}),
OneOrMany::One(OfferToRef::Named(Name::from_str("something").unwrap())),
));
assert!(maybe_generate_direct_offer_from_all(
&offer,
&offer_set,
&Name::from_str("something").unwrap()
)
.is_empty());
}
#[test]
fn test_expose_void_service_capability() {
let input = must_parse_cml!({
"expose": [
{
"service": "fuchsia.foo.Bar",
"from": [ "#non_existent_child" ],
"source_availability": "unknown",
},
],
});
let result = compile(&input, CompileOptions::default());
assert_matches!(result, Ok(_));
}
#[test]
fn test_aggregated_capabilities_must_use_same_availability_expose() {
let input = must_parse_cml!({
"expose": [
{
"service": "fuchsia.foo.Bar",
"from": [ "#a", "#b" ],
"availability": "optional",
},
],
"collections": [
{
"name": "a",
"durability": "transient",
},
{
"name": "b",
"durability": "transient",
},
],
});
let result = compile(&input, CompileOptions::default());
assert_matches!(result, Ok(_));
let input = must_parse_cml!({
"expose": [
{
"service": "fuchsia.foo.Bar",
"from": [ "#a", "#non_existent" ],
"source_availability": "unknown",
},
],
"collections": [
{
"name": "a",
"durability": "transient",
},
],
});
let result = compile(&input, CompileOptions::default());
assert_matches!(
result,
Err(Error::FidlValidator { errs: ErrorList { errs } })
if matches!(
&errs[..],
[
CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
]
if matches!(
&availabilities[..],
[ fdecl::Availability::Required, fdecl::Availability::Optional, ]
)
)
);
}
#[test]
fn test_aggregated_capabilities_must_use_same_availability_offer() {
let input = must_parse_cml!({
"offer": [
{
"service": "fuchsia.foo.Bar",
"from": [ "#a", "#b" ],
"to": "#c",
"availability": "optional",
},
],
"collections": [
{
"name": "a",
"durability": "transient",
},
{
"name": "b",
"durability": "transient",
},
],
"children": [
{
"name": "c",
"url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
},
],
});
let result = compile(&input, CompileOptions::default());
assert_matches!(result, Ok(_));
let input = must_parse_cml!({
"offer": [
{
"service": "fuchsia.foo.Bar",
"from": [ "#a", "#non_existent" ],
"to": "#c",
"source_availability": "unknown",
},
],
"collections": [
{
"name": "a",
"durability": "transient",
},
],
"children": [
{
"name": "c",
"url": "fuchsia-pkg://fuchsia.com/c/c#meta/c.cm",
},
],
});
let result = compile(&input, CompileOptions::default());
assert_matches!(
result,
Err(Error::FidlValidator { errs: ErrorList { errs } })
if matches!(
&errs[..],
[
CmFidlError::DifferentAvailabilityInAggregation(AvailabilityList(availabilities)),
]
if matches!(
&availabilities[..],
[ fdecl::Availability::Required, fdecl::Availability::Optional, ]
)
)
);
}
#[test]
fn test_compile_offer_to_all_exact_duplicate_disallowed() {
let input = must_parse_cml!({
"children": [
{
"name": "logger",
"url": "fuchsia-pkg://fuchsia.com/logger/stable#meta/logger.cm",
},
],
"offer": [
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "all",
},
{
"protocol": "fuchsia.logger.LogSink",
"from": "parent",
"to": "all",
},
],
});
assert_matches!(
compile(&input, CompileOptions::default()),
Err(Error::Validate { err, .. })
if &err == "Protocol(s) [\"fuchsia.logger.LogSink\"] offered to \"all\" multiple times"
);
}
#[test]
fn test_compile_use_config() {
let input = must_parse_cml!({
"use": [
{
"config": "fuchsia.config.Config",
"key" : "my_config",
"type": "bool",
}
],
});
let options = CompileOptions::new().config_package_path("fake.cvf");
let actual = compile(&input, options).unwrap();
let type_ = fdecl::ConfigType {
layout: fdecl::ConfigTypeLayout::Bool,
parameters: Some(vec![]),
constraints: vec![],
};
assert_eq!(
actual.uses.unwrap(),
vec![fdecl::Use::Config(fdecl::UseConfiguration {
source_name: Some("fuchsia.config.Config".to_string()),
source: Some(fdecl::Ref::Parent(fdecl::ParentRef {})),
target_name: Some("my_config".to_string()),
availability: Some(fdecl::Availability::Required),
type_: Some(type_.clone()),
..Default::default()
})]
);
assert_eq!(
actual.config.unwrap().fields.unwrap(),
[fdecl::ConfigField {
key: Some("my_config".to_string()),
type_: Some(type_),
mutability: Some(fdecl::ConfigMutability::default()),
..Default::default()
}]
.to_vec(),
);
}
#[test]
fn test_compile_use_config_optional_bad_type() {
let input = must_parse_cml!({
"use": [
{
"config": "fuchsia.config.Config",
"key" : "my_config",
"type": "bool",
"availability": "optional",
}
],
"config": {
"my_config": { "type": "int8"},
}
});
let options = CompileOptions::new().config_package_path("fake.cvf");
assert_matches!(
compile(&input, options),
Err(Error::Validate { err, .. })
if &err == "Use and config block differ on type for key 'my_config'"
);
}
#[test]
fn test_config_source_from_package() {
let input = must_parse_cml!({
"use": [
{
"config": "fuchsia.config.Config",
"key" : "my_config",
"type": "bool",
"availability": "optional",
}
],
"config": {
"my_config": { "type": "bool"},
}
});
let options = CompileOptions::new().config_package_path("fake.cvf");
let decl = compile(&input, options).unwrap();
let config = decl.config.unwrap();
assert_eq!(
config.value_source,
Some(fdecl::ConfigValueSource::PackagePath("fake.cvf".into()))
);
}
#[test]
fn test_config_source_from_capabilities() {
let input = must_parse_cml!({
"use": [
{
"config": "fuchsia.config.Config",
"key" : "my_config",
"type": "bool",
}
],
});
let options = CompileOptions::new().config_package_path("fake.cvf");
let decl = compile(&input, options).unwrap();
let config = decl.config.unwrap();
assert_eq!(
config.value_source,
Some(
fdecl::ConfigValueSource::Capabilities(fdecl::ConfigSourceCapabilities::default())
)
);
}
#[test]
fn test_config_default() {
let input = must_parse_cml!({
"use": [
{
"config": "fuchsia.config.Config",
"key" : "my_config",
"type": "bool",
"availability": "optional",
"default": true
}
],
});
let options = CompileOptions::new().config_package_path("fake.cvf");
let decl = compile(&input, options).unwrap();
assert_matches!(
decl.uses.as_ref().unwrap()[0],
fdecl::Use::Config(fdecl::UseConfiguration {
default: Some(fdecl::ConfigValue::Single(fdecl::ConfigSingleValue::Bool(true))),
..
})
);
}
#[test]
fn test_config_default_bad_type() {
let input = must_parse_cml!({
"use": [
{
"config": "fuchsia.config.Config",
"key" : "my_config",
"type": "bool",
"availability": "optional",
"default": 5
}
],
});
let options = CompileOptions::new().config_package_path("fake.cvf");
assert_matches!(compile(&input, options), Err(Error::InvalidArgs(_)));
}
#[test]
fn test_compile_protocol_delivery_type() {
let input = must_parse_cml!({
"capabilities": [
{
"protocol": "fuchsia.echo.Echo",
"delivery": "on_readable",
}
],
});
let features = FeatureSet::from(vec![Feature::DeliveryType]);
let options = CompileOptions::new().features(&features);
let decl = compile(&input, options).unwrap();
assert_matches!(
decl.capabilities.as_ref().unwrap()[0],
fdecl::Capability::Protocol(fdecl::Protocol {
delivery: Some(fdecl::DeliveryType::OnReadable),
..
})
);
}
#[test]
fn test_compile_protocol_setting_delivery_type_requires_feature_flag() {
let input = must_parse_cml!({
"capabilities": [
{
"protocol": "fuchsia.echo.Echo",
"delivery": "on_readable",
}
],
});
assert_matches!(
compile(&input, CompileOptions::new()),
Err(Error::RestrictedFeature(feature))
if feature == "delivery_type"
);
}
}