blob: 5b334c7276d810bd1fd312e6598115b3d64ed29b [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
cm_fidl_validator,
failure::Fail,
fidl_fuchsia_data as fdata, fidl_fuchsia_sys2 as fsys,
std::collections::HashMap,
std::convert::{From, TryFrom, TryInto},
std::fmt,
};
pub mod data;
/// Converts a fidl object into its corresponding native representation.
pub trait FidlIntoNative<T> {
fn fidl_into_native(self) -> T;
}
pub trait NativeIntoFidl<T> {
fn native_into_fidl(self) -> T;
}
/// Generates `FidlIntoNative` and `NativeIntoFidl` implementations for a basic type from
/// `Option<type>` that respectively unwraps the Option and wraps the internal value in a `Some()`.
macro_rules! fidl_translations_opt_type {
($into_type:ty) => {
impl FidlIntoNative<$into_type> for Option<$into_type> {
fn fidl_into_native(self) -> $into_type {
self.unwrap()
}
}
impl NativeIntoFidl<Option<$into_type>> for $into_type {
fn native_into_fidl(self) -> Option<$into_type> {
Some(self)
}
}
};
}
/// Generates `FidlIntoNative` and `NativeIntoFidl` implementations that leaves the input unchanged.
macro_rules! fidl_translations_identical {
($into_type:ty) => {
impl FidlIntoNative<$into_type> for $into_type {
fn fidl_into_native(self) -> $into_type {
self
}
}
impl NativeIntoFidl<$into_type> for $into_type {
fn native_into_fidl(self) -> Self {
self
}
}
};
}
/// Generates a struct with a `FidlIntoNative` implementation that calls `fidl_into_native()` on each
/// field.
/// - `into_type` is the name of the struct and the into type for the conversion.
/// - `into_ident` must be identical to `into_type`.
/// - `from_type` is the from type for the conversion.
/// - `from_ident` must be identical to `from_type`.
/// - `field: type` form a list of fields and their types for the generated struct.
macro_rules! fidl_into_struct {
($into_type:ty, $into_ident:ident, $from_type:ty, $from_path:path,
{ $( $field:ident: $type:ty, )+ } ) => {
#[derive(Debug, Clone, PartialEq)]
pub struct $into_ident {
$(
pub $field: $type,
)+
}
impl FidlIntoNative<$into_type> for $from_type {
fn fidl_into_native(self) -> $into_type {
$into_ident { $( $field: self.$field.fidl_into_native(), )+ }
}
}
impl NativeIntoFidl<$from_type> for $into_type {
fn native_into_fidl(self) -> $from_type {
use $from_path as from_ident;
from_ident { $( $field: self.$field.native_into_fidl(), )+ }
}
}
}
}
/// Generates an enum with a `FidlIntoNative` implementation that calls `fidl_into_native()` on each
/// field.
/// - `into_type` is the name of the enum and the into type for the conversion.
/// - `into_ident` must be identical to `into_type`.
/// - `from_type` is the from type for the conversion.
/// - `from_ident` must be identical to `from_type`.
/// - `variant(type)` form a list of variants and their types for the generated enum.
macro_rules! fidl_into_enum {
($into_type:ty, $into_ident:ident, $from_type:ty, $from_path:path,
{ $( $variant:ident($type:ty), )+ } ) => {
#[derive(Debug, Clone, PartialEq)]
pub enum $into_ident {
$(
$variant($type),
)+
}
impl FidlIntoNative<$into_type> for $from_type {
fn fidl_into_native(self) -> $into_type {
use $from_path as from_ident;
match self {
$(
from_ident::$variant(e) => $into_ident::$variant(e.fidl_into_native()),
)+
from_ident::__UnknownVariant {..} => { panic!("invalid variant") }
}
}
}
impl NativeIntoFidl<$from_type> for $into_type {
fn native_into_fidl(self) -> $from_type {
use $from_path as from_ident;
match self {
$(
$into_ident::$variant(e) => from_ident::$variant(e.native_into_fidl()),
)+
}
}
}
}
}
/// Generates `FidlIntoNative` and `NativeIntoFidl` implementations between `Vec` and
/// `Option<Vec>`.
/// - `into_type` is the name of the struct and the into type for the conversion.
/// - `from_type` is the from type for the conversion.
macro_rules! fidl_into_vec {
($into_type:ty, $from_type:ty) => {
impl FidlIntoNative<Vec<$into_type>> for Option<Vec<$from_type>> {
fn fidl_into_native(self) -> Vec<$into_type> {
if let Some(from) = self {
from.into_iter().map(|e: $from_type| e.fidl_into_native()).collect()
} else {
vec![]
}
}
}
impl NativeIntoFidl<Option<Vec<$from_type>>> for Vec<$into_type> {
fn native_into_fidl(self) -> Option<Vec<$from_type>> {
if self.is_empty() {
None
} else {
Some(self.into_iter().map(|e: $into_type| e.native_into_fidl()).collect())
}
}
}
};
}
#[derive(Debug, PartialEq)]
pub struct ComponentDecl {
pub program: Option<fdata::Dictionary>,
pub uses: Vec<UseDecl>,
pub exposes: Vec<ExposeDecl>,
pub offers: Vec<OfferDecl>,
pub children: Vec<ChildDecl>,
pub collections: Vec<CollectionDecl>,
pub facets: Option<fdata::Dictionary>,
}
impl FidlIntoNative<ComponentDecl> for fsys::ComponentDecl {
fn fidl_into_native(self) -> ComponentDecl {
ComponentDecl {
program: self.program.fidl_into_native(),
uses: self.uses.fidl_into_native(),
exposes: self.exposes.fidl_into_native(),
offers: self.offers.fidl_into_native(),
children: self.children.fidl_into_native(),
collections: self.collections.fidl_into_native(),
facets: self.facets.fidl_into_native(),
}
}
}
impl NativeIntoFidl<fsys::ComponentDecl> for ComponentDecl {
fn native_into_fidl(self) -> fsys::ComponentDecl {
fsys::ComponentDecl {
program: self.program.native_into_fidl(),
uses: self.uses.native_into_fidl(),
exposes: self.exposes.native_into_fidl(),
offers: self.offers.native_into_fidl(),
children: self.children.native_into_fidl(),
collections: self.collections.native_into_fidl(),
facets: self.facets.native_into_fidl(),
storage: None,
}
}
}
impl Clone for ComponentDecl {
fn clone(&self) -> Self {
ComponentDecl {
program: data::clone_option_dictionary(&self.program),
uses: self.uses.clone(),
exposes: self.exposes.clone(),
offers: self.offers.clone(),
children: self.children.clone(),
collections: self.collections.clone(),
facets: data::clone_option_dictionary(&self.facets),
}
}
}
impl ComponentDecl {
/// Returns the `ExposeDecl` that exposes `capability`, if it exists.
pub fn find_expose_source<'a>(&'a self, capability: &Capability) -> Option<&'a ExposeDecl> {
self.exposes.iter().find(|&e| match (capability, e) {
(Capability::Service(p), ExposeDecl::Service(d)) => d.target_path == *p,
(Capability::Directory(p), ExposeDecl::Directory(d)) => d.target_path == *p,
_ => false,
})
}
/// Returns the `OfferDecl` that offers `capability` to `child_name`, if it exists.
pub fn find_offer_source<'a>(
&'a self,
capability: &Capability,
child_name: &str,
) -> Option<&'a OfferDecl> {
self.offers.iter().find(|&offer| match (capability, offer) {
(Capability::Service(p), OfferDecl::Service(d)) => d
.targets
.iter()
.find(|&e| {
if let OfferDest::Child(target_child_name) = &e.dest {
e.target_path == *p && target_child_name == child_name
} else {
false
}
})
.is_some(),
(Capability::Directory(p), OfferDecl::Directory(d)) => d
.targets
.iter()
.find(|&e| {
if let OfferDest::Child(target_child_name) = &e.dest {
e.target_path == *p && target_child_name == child_name
} else {
false
}
})
.is_some(),
_ => false,
})
}
}
fidl_into_enum!(UseDecl, UseDecl, fsys::UseDecl, fsys::UseDecl,
{
Service(UseServiceDecl),
Directory(UseDirectoryDecl),
});
fidl_into_struct!(UseServiceDecl, UseServiceDecl, fsys::UseServiceDecl, fsys::UseServiceDecl,
{
source_path: CapabilityPath,
target_path: CapabilityPath,
});
fidl_into_struct!(UseDirectoryDecl, UseDirectoryDecl, fsys::UseDirectoryDecl,
fsys::UseDirectoryDecl,
{
source_path: CapabilityPath,
target_path: CapabilityPath,
});
fidl_into_enum!(ExposeDecl, ExposeDecl, fsys::ExposeDecl, fsys::ExposeDecl,
{
Service(ExposeServiceDecl),
Directory(ExposeDirectoryDecl),
});
fidl_into_struct!(ExposeServiceDecl, ExposeServiceDecl, fsys::ExposeServiceDecl,
fsys::ExposeServiceDecl,
{
source: ExposeSource,
source_path: CapabilityPath,
target_path: CapabilityPath,
});
fidl_into_struct!(ExposeDirectoryDecl, ExposeDirectoryDecl, fsys::ExposeDirectoryDecl,
fsys::ExposeDirectoryDecl,
{
source: ExposeSource,
source_path: CapabilityPath,
target_path: CapabilityPath,
});
fidl_into_enum!(OfferDecl, OfferDecl, fsys::OfferDecl, fsys::OfferDecl,
{
Service(OfferServiceDecl),
Directory(OfferDirectoryDecl),
});
fidl_into_struct!(OfferServiceDecl, OfferServiceDecl, fsys::OfferServiceDecl,
fsys::OfferServiceDecl,
{
source: OfferSource,
source_path: CapabilityPath,
targets: Vec<OfferTarget>,
});
fidl_into_struct!(OfferDirectoryDecl, OfferDirectoryDecl, fsys::OfferDirectoryDecl,
fsys::OfferDirectoryDecl,
{
source: OfferSource,
source_path: CapabilityPath,
targets: Vec<OfferTarget>,
});
fidl_into_struct!(OfferTarget, OfferTarget, fsys::OfferTarget, fsys::OfferTarget,
{
target_path: CapabilityPath,
dest: OfferDest,
});
fidl_into_struct!(ChildDecl, ChildDecl, fsys::ChildDecl, fsys::ChildDecl,
{
name: String,
url: String,
startup: fsys::StartupMode,
});
fidl_into_struct!(CollectionDecl, CollectionDecl, fsys::CollectionDecl, fsys::CollectionDecl,
{
name: String,
durability: fsys::Durability,
});
fidl_into_vec!(UseDecl, fsys::UseDecl);
fidl_into_vec!(ExposeDecl, fsys::ExposeDecl);
fidl_into_vec!(OfferDecl, fsys::OfferDecl);
fidl_into_vec!(ChildDecl, fsys::ChildDecl);
fidl_into_vec!(CollectionDecl, fsys::CollectionDecl);
fidl_into_vec!(OfferTarget, fsys::OfferTarget);
fidl_translations_opt_type!(String);
fidl_translations_opt_type!(fsys::StartupMode);
fidl_translations_opt_type!(fsys::Durability);
fidl_translations_opt_type!(fdata::Dictionary);
fidl_translations_identical!(Option<fdata::Dictionary>);
#[derive(Debug, Clone, PartialEq)]
pub struct CapabilityPath {
pub dirname: String,
pub basename: String,
}
impl CapabilityPath {
pub fn to_string(&self) -> String {
format!("{}", self)
}
}
impl TryFrom<&str> for CapabilityPath {
type Error = Error;
fn try_from(path: &str) -> Result<CapabilityPath, Error> {
if !path.starts_with("/") {
// Paths must be absolute.
return Err(Error::InvalidCapabilityPath { raw: path.to_string() });
}
let mut parts = vec![];
let mut parts_iter = path.split("/");
// Skip empty component generated by first '/'.
parts_iter.next();
for part in parts_iter {
if part.is_empty() {
// Paths may not have empty components.
return Err(Error::InvalidCapabilityPath { raw: path.to_string() });
}
parts.push(part);
}
if parts.is_empty() {
// Root paths are not allowed.
return Err(Error::InvalidCapabilityPath { raw: path.to_string() });
}
Ok(CapabilityPath {
dirname: format!("/{}", parts[..parts.len() - 1].join("/")),
basename: parts.last().unwrap().to_string(),
})
}
}
impl fmt::Display for CapabilityPath {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if &self.dirname == "/" {
write!(f, "/{}", self.basename)
} else {
write!(f, "{}/{}", self.dirname, self.basename)
}
}
}
// TODO: Runners and third parties can use this to parse `program` or `facets`.
impl FidlIntoNative<Option<HashMap<String, Value>>> for Option<fdata::Dictionary> {
fn fidl_into_native(self) -> Option<HashMap<String, Value>> {
self.map(|d| from_fidl_dict(d))
}
}
impl FidlIntoNative<CapabilityPath> for Option<String> {
fn fidl_into_native(self) -> CapabilityPath {
let s: &str = &self.unwrap();
s.try_into().expect("invalid capability path")
}
}
impl NativeIntoFidl<Option<String>> for CapabilityPath {
fn native_into_fidl(self) -> Option<String> {
Some(self.to_string())
}
}
#[derive(Debug, PartialEq)]
pub enum Value {
Bit(bool),
Inum(i64),
Fnum(f64),
Str(String),
Vec(Vec<Value>),
Dict(HashMap<String, Value>),
Null,
}
impl FidlIntoNative<Value> for Option<Box<fdata::Value>> {
fn fidl_into_native(self) -> Value {
match self {
Some(v) => match *v {
fdata::Value::Bit(b) => Value::Bit(b),
fdata::Value::Inum(i) => Value::Inum(i),
fdata::Value::Fnum(f) => Value::Fnum(f),
fdata::Value::Str(s) => Value::Str(s),
fdata::Value::Vec(v) => Value::Vec(from_fidl_vec(v)),
fdata::Value::Dict(d) => Value::Dict(from_fidl_dict(d)),
},
None => Value::Null,
}
}
}
fn from_fidl_vec(vec: fdata::Vector) -> Vec<Value> {
vec.values.into_iter().map(|v| v.fidl_into_native()).collect()
}
fn from_fidl_dict(dict: fdata::Dictionary) -> HashMap<String, Value> {
dict.entries.into_iter().map(|e| (e.key, e.value.fidl_into_native())).collect()
}
/// Generic container for any capability type. Doesn't map onto any fidl declaration, but useful as
/// an intermediate representation.
#[derive(Debug, Clone, PartialEq)]
pub enum Capability {
Service(CapabilityPath),
Directory(CapabilityPath),
}
impl Capability {
pub fn path(&self) -> &CapabilityPath {
match self {
Capability::Service(s) => s,
Capability::Directory(d) => d,
}
}
}
impl fmt::Display for Capability {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Capability::Service(s) => write!(f, "service at {}", s),
Capability::Directory(d) => write!(f, "directory at {}", d),
}
}
}
impl From<UseDecl> for Capability {
fn from(d: UseDecl) -> Self {
match d {
UseDecl::Service(d) => d.into(),
UseDecl::Directory(d) => d.into(),
}
}
}
impl From<UseServiceDecl> for Capability {
fn from(d: UseServiceDecl) -> Self {
Capability::Service(d.source_path)
}
}
impl From<UseDirectoryDecl> for Capability {
fn from(d: UseDirectoryDecl) -> Self {
Capability::Directory(d.source_path)
}
}
impl From<ExposeDecl> for Capability {
fn from(d: ExposeDecl) -> Self {
match d {
ExposeDecl::Service(d) => d.into(),
ExposeDecl::Directory(d) => d.into(),
}
}
}
impl From<ExposeServiceDecl> for Capability {
fn from(d: ExposeServiceDecl) -> Self {
Capability::Service(d.source_path)
}
}
impl From<ExposeDirectoryDecl> for Capability {
fn from(d: ExposeDirectoryDecl) -> Self {
Capability::Directory(d.source_path)
}
}
impl From<OfferDecl> for Capability {
fn from(d: OfferDecl) -> Self {
match d {
OfferDecl::Service(d) => d.into(),
OfferDecl::Directory(d) => d.into(),
}
}
}
impl From<OfferServiceDecl> for Capability {
fn from(d: OfferServiceDecl) -> Self {
Capability::Service(d.source_path)
}
}
impl From<OfferDirectoryDecl> for Capability {
fn from(d: OfferDirectoryDecl) -> Self {
Capability::Directory(d.source_path)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ExposeSource {
Myself,
Child(String),
}
impl FidlIntoNative<ExposeSource> for Option<fsys::ExposeSource> {
fn fidl_into_native(self) -> ExposeSource {
match self.unwrap() {
fsys::ExposeSource::Myself(_) => ExposeSource::Myself,
fsys::ExposeSource::Child(c) => ExposeSource::Child(c.name.unwrap()),
fsys::ExposeSource::__UnknownVariant { .. } => panic!("unknown ExposeSource variant"),
}
}
}
impl NativeIntoFidl<Option<fsys::ExposeSource>> for ExposeSource {
fn native_into_fidl(self) -> Option<fsys::ExposeSource> {
Some(match self {
ExposeSource::Myself => fsys::ExposeSource::Myself(fsys::SelfRef {}),
ExposeSource::Child(child_name) => {
fsys::ExposeSource::Child(fsys::ChildRef { name: Some(child_name) })
}
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum OfferSource {
Realm,
Myself,
Child(String),
}
impl FidlIntoNative<OfferSource> for Option<fsys::OfferSource> {
fn fidl_into_native(self) -> OfferSource {
match self.unwrap() {
fsys::OfferSource::Realm(_) => OfferSource::Realm,
fsys::OfferSource::Myself(_) => OfferSource::Myself,
fsys::OfferSource::Child(c) => OfferSource::Child(c.name.unwrap()),
fsys::OfferSource::__UnknownVariant { .. } => panic!("unknown OfferSource variant"),
}
}
}
impl NativeIntoFidl<Option<fsys::OfferSource>> for OfferSource {
fn native_into_fidl(self) -> Option<fsys::OfferSource> {
Some(match self {
OfferSource::Realm => fsys::OfferSource::Realm(fsys::RealmRef {}),
OfferSource::Myself => fsys::OfferSource::Myself(fsys::SelfRef {}),
OfferSource::Child(child_name) => {
fsys::OfferSource::Child(fsys::ChildRef { name: Some(child_name) })
}
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum OfferDest {
Child(String),
Collection(String),
}
impl FidlIntoNative<OfferDest> for Option<fsys::OfferDest> {
fn fidl_into_native(self) -> OfferDest {
match self.unwrap() {
fsys::OfferDest::Child(c) => OfferDest::Child(c.name.unwrap()),
fsys::OfferDest::Collection(c) => OfferDest::Collection(c.name.unwrap()),
fsys::OfferDest::__UnknownVariant { .. } => panic!("unknown OfferDest variant"),
}
}
}
impl NativeIntoFidl<Option<fsys::OfferDest>> for OfferDest {
fn native_into_fidl(self) -> Option<fsys::OfferDest> {
Some(match self {
OfferDest::Child(child_name) => {
fsys::OfferDest::Child(fsys::ChildRef { name: Some(child_name) })
}
OfferDest::Collection(collection_name) => {
fsys::OfferDest::Collection(fsys::CollectionRef { name: Some(collection_name) })
}
})
}
}
/// Converts the contents of a CM-FIDL declaration and produces the equivalent CM-Rust
/// struct.
/// This function applies cm_fidl_validator to check correctness.
impl TryFrom<fsys::ComponentDecl> for ComponentDecl {
type Error = Error;
fn try_from(decl: fsys::ComponentDecl) -> Result<Self, Self::Error> {
cm_fidl_validator::validate(&decl).map_err(|err| Error::Validate { err })?;
Ok(decl.fidl_into_native())
}
}
// Converts the contents of a CM-Rust declaration into a CM_FIDL declaration
impl TryFrom<ComponentDecl> for fsys::ComponentDecl {
type Error = Error;
fn try_from(decl: ComponentDecl) -> Result<Self, Self::Error> {
Ok(decl.native_into_fidl())
}
}
/// Errors produced by cm_rust.
#[derive(Debug, Fail)]
pub enum Error {
#[fail(display = "Fidl validation failed: {}", err)]
Validate {
#[fail(cause)]
err: cm_fidl_validator::ErrorList,
},
#[fail(display = "Invalid capability path: {}", raw)]
InvalidCapabilityPath { raw: String },
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! test_from {
(
$(
$test_name:ident => {
input = $input:expr,
result = $result:expr,
},
)+
) => {
$(
#[test]
fn $test_name() {
test_from_helper($input, $result);
}
)+
}
}
fn test_from_helper<T, U>(input: Vec<T>, result: Vec<U>)
where
U: From<T> + std::cmp::PartialEq + std::fmt::Debug,
{
let res: Vec<U> = input.into_iter().map(|e| e.into()).collect();
assert_eq!(res, result);
}
macro_rules! test_try_from_decl {
(
$(
$test_name:ident => {
input = $input:expr,
result = $result:expr,
},
)+
) => {
$(
#[test]
fn $test_name() {
{
let res = ComponentDecl::try_from($input).expect("try_from failed");
assert_eq!(res, $result);
}
{
let res = fsys::ComponentDecl::try_from($result).expect("try_from failed");
assert_eq!(res, $input);
}
}
)+
}
}
macro_rules! test_fidl_into_and_from {
(
$(
$test_name:ident => {
input = $input:expr,
result = $result:expr,
},
)+
) => {
$(
#[test]
fn $test_name() {
{
let res: Vec<_> =
$input.into_iter().map(|e| e.fidl_into_native()).collect();
assert_eq!(res, $result);
}
{
let res: Vec<_> =
$result.into_iter().map(|e| e.native_into_fidl()).collect();
assert_eq!(res, $input);
}
}
)+
}
}
macro_rules! test_fidl_into {
(
$(
$test_name:ident => {
input = $input:expr,
result = $result:expr,
},
)+
) => {
$(
#[test]
fn $test_name() {
test_fidl_into_helper($input, $result);
}
)+
}
}
fn test_fidl_into_helper<T, U>(input: T, expected_res: U)
where
T: FidlIntoNative<U>,
U: std::cmp::PartialEq + std::fmt::Debug,
{
let res: U = input.fidl_into_native();
assert_eq!(res, expected_res);
}
macro_rules! test_capability_path {
(
$(
$test_name:ident => {
input = $input:expr,
result = $result:expr,
},
)+
) => {
$(
#[test]
fn $test_name() {
test_capability_path_helper($input, $result);
}
)+
}
}
fn test_capability_path_helper(input: &str, result: Result<CapabilityPath, Error>) {
let res = CapabilityPath::try_from(input);
assert_eq!(format!("{:?}", res), format!("{:?}", result));
if let Ok(p) = res {
assert_eq!(&p.to_string(), input);
}
}
test_try_from_decl! {
try_from_empty => {
input = fsys::ComponentDecl {
program: None,
uses: None,
exposes: None,
offers: None,
children: None,
collections: None,
facets: None,
storage: None,
},
result = ComponentDecl {
program: None,
uses: vec![],
exposes: vec![],
offers: vec![],
children: vec![],
collections: vec![],
facets: None,
},
},
try_from_all => {
input = fsys::ComponentDecl {
program: Some(fdata::Dictionary{entries: vec![
fdata::Entry{
key: "binary".to_string(),
value: Some(Box::new(fdata::Value::Str("bin/app".to_string()))),
},
]}),
uses: Some(vec![
fsys::UseDecl::Service(fsys::UseServiceDecl {
source_path: Some("/svc/netstack".to_string()),
target_path: Some("/svc/mynetstack".to_string()),
}),
fsys::UseDecl::Directory(fsys::UseDirectoryDecl {
source_path: Some("/data/dir".to_string()),
target_path: Some("/data".to_string()),
}),
]),
exposes: Some(vec![
fsys::ExposeDecl::Service(fsys::ExposeServiceDecl {
source: Some(fsys::ExposeSource::Child(fsys::ChildRef {
name: Some("netstack".to_string()),
})),
source_path: Some("/svc/netstack".to_string()),
target_path: Some("/svc/mynetstack".to_string()),
}),
fsys::ExposeDecl::Directory(fsys::ExposeDirectoryDecl {
source: Some(fsys::ExposeSource::Child(fsys::ChildRef {
name: Some("netstack".to_string()),
})),
source_path: Some("/data/dir".to_string()),
target_path: Some("/data".to_string()),
}),
]),
offers: Some(vec![
fsys::OfferDecl::Service(fsys::OfferServiceDecl {
source: Some(fsys::OfferSource::Realm(fsys::RealmRef {})),
source_path: Some("/svc/netstack".to_string()),
targets: Some(vec![
fsys::OfferTarget{
target_path: Some("/svc/mynetstack".to_string()),
dest: Some(fsys::OfferDest::Child(
fsys::ChildRef { name: Some("echo".to_string()) }
)),
},
]),
}),
fsys::OfferDecl::Directory(fsys::OfferDirectoryDecl {
source: Some(fsys::OfferSource::Realm(fsys::RealmRef {})),
source_path: Some("/data/dir".to_string()),
targets: Some(vec![
fsys::OfferTarget{
target_path: Some("/data".to_string()),
dest: Some(fsys::OfferDest::Collection(
fsys::CollectionRef { name: Some("modular".to_string()) }
)),
},
]),
}),
]),
children: Some(vec![
fsys::ChildDecl {
name: Some("netstack".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cm"
.to_string()),
startup: Some(fsys::StartupMode::Lazy),
},
fsys::ChildDecl {
name: Some("echo".to_string()),
url: Some("fuchsia-pkg://fuchsia.com/echo#meta/echo.cm"
.to_string()),
startup: Some(fsys::StartupMode::Eager),
},
]),
collections: Some(vec![
fsys::CollectionDecl {
name: Some("modular".to_string()),
durability: Some(fsys::Durability::Persistent),
},
fsys::CollectionDecl {
name: Some("tests".to_string()),
durability: Some(fsys::Durability::Transient),
},
]),
facets: Some(fdata::Dictionary{entries: vec![
fdata::Entry{
key: "author".to_string(),
value: Some(Box::new(fdata::Value::Str("Fuchsia".to_string()))),
},
]}),
storage: None,
},
result = {
ComponentDecl {
program: Some(fdata::Dictionary{entries: vec![
fdata::Entry{
key: "binary".to_string(),
value: Some(Box::new(fdata::Value::Str("bin/app".to_string()))),
},
]}),
uses: vec![
UseDecl::Service(UseServiceDecl {
source_path: "/svc/netstack".try_into().unwrap(),
target_path: "/svc/mynetstack".try_into().unwrap(),
}),
UseDecl::Directory(UseDirectoryDecl {
source_path: "/data/dir".try_into().unwrap(),
target_path: "/data".try_into().unwrap(),
}),
],
exposes: vec![
ExposeDecl::Service(ExposeServiceDecl {
source: ExposeSource::Child("netstack".to_string()),
source_path: "/svc/netstack".try_into().unwrap(),
target_path: "/svc/mynetstack".try_into().unwrap(),
}),
ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Child("netstack".to_string()),
source_path: "/data/dir".try_into().unwrap(),
target_path: "/data".try_into().unwrap(),
}),
],
offers: vec![
OfferDecl::Service(OfferServiceDecl {
source: OfferSource::Realm,
source_path: "/svc/netstack".try_into().unwrap(),
targets: vec![
OfferTarget {
target_path: "/svc/mynetstack".try_into().unwrap(),
dest: OfferDest::Child("echo".to_string()),
},
],
}),
OfferDecl::Directory(OfferDirectoryDecl {
source: OfferSource::Realm,
source_path: "/data/dir".try_into().unwrap(),
targets: vec![
OfferTarget {
target_path: "/data".try_into().unwrap(),
dest: OfferDest::Collection("modular".to_string()),
},
],
}),
],
children: vec![
ChildDecl {
name: "netstack".to_string(),
url: "fuchsia-pkg://fuchsia.com/netstack#meta/netstack.cm".to_string(),
startup: fsys::StartupMode::Lazy,
},
ChildDecl {
name: "echo".to_string(),
url: "fuchsia-pkg://fuchsia.com/echo#meta/echo.cm".to_string(),
startup: fsys::StartupMode::Eager,
},
],
collections: vec![
CollectionDecl {
name: "modular".to_string(),
durability: fsys::Durability::Persistent,
},
CollectionDecl {
name: "tests".to_string(),
durability: fsys::Durability::Transient,
},
],
facets: Some(fdata::Dictionary{entries: vec![
fdata::Entry{
key: "author".to_string(),
value: Some(Box::new(fdata::Value::Str("Fuchsia".to_string()))),
},
]}),
}
},
},
}
test_capability_path! {
capability_path_one_part => {
input = "/foo",
result = Ok(CapabilityPath{dirname: "/".to_string(), basename: "foo".to_string()}),
},
capability_path_two_parts => {
input = "/foo/bar",
result = Ok(CapabilityPath{dirname: "/foo".to_string(), basename: "bar".to_string()}),
},
capability_path_many_parts => {
input = "/foo/bar/long/path",
result = Ok(CapabilityPath{
dirname: "/foo/bar/long".to_string(),
basename: "path".to_string()
}),
},
capability_path_invalid_empty_part => {
input = "/foo/bar//long/path",
result = Err(Error::InvalidCapabilityPath{raw: "/foo/bar//long/path".to_string()}),
},
capability_path_invalid_empty => {
input = "",
result = Err(Error::InvalidCapabilityPath{raw: "".to_string()}),
},
capability_path_invalid_root => {
input = "/",
result = Err(Error::InvalidCapabilityPath{raw: "/".to_string()}),
},
capability_path_invalid_relative => {
input = "foo/bar",
result = Err(Error::InvalidCapabilityPath{raw: "foo/bar".to_string()}),
},
capability_path_invalid_trailing => {
input = "/foo/bar/",
result = Err(Error::InvalidCapabilityPath{raw: "/foo/bar/".to_string()}),
},
}
test_from! {
from_use_capability => {
input = vec![
UseDecl::Service(UseServiceDecl {
source_path: CapabilityPath::try_from("/foo/bar").unwrap(),
target_path: CapabilityPath::try_from("/blah").unwrap(),
}),
UseDecl::Directory(UseDirectoryDecl {
source_path: CapabilityPath::try_from("/foo/bar").unwrap(),
target_path: CapabilityPath::try_from("/blah").unwrap(),
}),
],
result = vec![
Capability::Service(CapabilityPath::try_from("/foo/bar").unwrap()),
Capability::Directory(CapabilityPath::try_from("/foo/bar").unwrap()),
],
},
from_expose_capability => {
input = vec![
ExposeDecl::Service(ExposeServiceDecl {
source: ExposeSource::Myself,
source_path: CapabilityPath::try_from("/foo/bar").unwrap(),
target_path: CapabilityPath::try_from("/blah").unwrap(),
}),
ExposeDecl::Directory(ExposeDirectoryDecl {
source: ExposeSource::Myself,
source_path: CapabilityPath::try_from("/foo/bar").unwrap(),
target_path: CapabilityPath::try_from("/blah").unwrap(),
}),
],
result = vec![
Capability::Service(CapabilityPath::try_from("/foo/bar").unwrap()),
Capability::Directory(CapabilityPath::try_from("/foo/bar").unwrap()),
],
},
from_offer_capability => {
input = vec![
OfferDecl::Service(OfferServiceDecl {
source: OfferSource::Myself,
source_path: CapabilityPath::try_from("/foo/bar").unwrap(),
targets: vec![
OfferTarget {
target_path: CapabilityPath::try_from("/blah").unwrap(),
dest: OfferDest::Child("child".to_string()),
}
],
}),
OfferDecl::Directory(OfferDirectoryDecl {
source: OfferSource::Myself,
source_path: CapabilityPath::try_from("/foo/bar").unwrap(),
targets: vec![
OfferTarget {
target_path: CapabilityPath::try_from("/blah").unwrap(),
dest: OfferDest::Child("child".to_string()),
}
],
}),
],
result = vec![
Capability::Service(CapabilityPath::try_from("/foo/bar").unwrap()),
Capability::Directory(CapabilityPath::try_from("/foo/bar").unwrap()),
],
},
}
test_fidl_into_and_from! {
fidl_into_and_from_expose_source => {
input = vec![
Some(fsys::ExposeSource::Myself(fsys::SelfRef {})),
Some(fsys::ExposeSource::Child(fsys::ChildRef {
name: Some("foo".to_string()),
})),
],
result = vec![
ExposeSource::Myself,
ExposeSource::Child("foo".to_string()),
],
},
fidl_into_and_from_offer_source => {
input = vec![
Some(fsys::OfferSource::Realm(fsys::RealmRef {})),
Some(fsys::OfferSource::Myself(fsys::SelfRef {})),
Some(fsys::OfferSource::Child(fsys::ChildRef {
name: Some("foo".to_string()),
})),
],
result = vec![
OfferSource::Realm,
OfferSource::Myself,
OfferSource::Child("foo".to_string()),
],
},
}
test_fidl_into! {
fidl_into_dictionary => {
input = {
let dict_inner = fdata::Dictionary{entries: vec![
fdata::Entry{
key: "string".to_string(),
value: Some(Box::new(fdata::Value::Str("bar".to_string()))),
},
]};
let vector = fdata::Vector{values: vec![
Some(Box::new(fdata::Value::Dict(dict_inner))),
Some(Box::new(fdata::Value::Inum(-42)))
]};
let dict_outer = fdata::Dictionary{entries: vec![
fdata::Entry{
key: "array".to_string(),
value: Some(Box::new(fdata::Value::Vec(vector))),
},
]};
let dict = fdata::Dictionary {entries: vec![
fdata::Entry {
key: "bool".to_string(),
value: Some(Box::new(fdata::Value::Bit(true))),
},
fdata::Entry {
key: "dict".to_string(),
value: Some(Box::new(fdata::Value::Dict(dict_outer))),
},
fdata::Entry {
key: "float".to_string(),
value: Some(Box::new(fdata::Value::Fnum(3.14))),
},
fdata::Entry {
key: "int".to_string(),
value: Some(Box::new(fdata::Value::Inum(-42))),
},
fdata::Entry {
key: "null".to_string(),
value: None,
},
fdata::Entry {
key: "string".to_string(),
value: Some(Box::new(fdata::Value::Str("bar".to_string()))),
},
]};
Some(dict)
},
result = {
let mut dict_inner = HashMap::new();
dict_inner.insert("string".to_string(), Value::Str("bar".to_string()));
let mut dict_outer = HashMap::new();
let vector = vec![Value::Dict(dict_inner), Value::Inum(-42)];
dict_outer.insert("array".to_string(), Value::Vec(vector));
let mut dict: HashMap<String, Value> = HashMap::new();
dict.insert("bool".to_string(), Value::Bit(true));
dict.insert("float".to_string(), Value::Fnum(3.14));
dict.insert("int".to_string(), Value::Inum(-42));
dict.insert("string".to_string(), Value::Str("bar".to_string()));
dict.insert("dict".to_string(), Value::Dict(dict_outer));
dict.insert("null".to_string(), Value::Null);
Some(dict)
},
},
}
}