blob: de05475ed519ad2b256cf8e97fa2087007d4ac63 [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 std::fmt;
/// A child moniker locally identifies a child component instance using the name assigned by
/// its parent. It is a building block for more complex monikers.
///
/// Display notation: "#name".
///
/// TODO: Add a mechanism for representing children grouped into collections by index.
#[derive(Eq, PartialEq, Debug)]
pub struct ChildMoniker {
name: String,
}
impl ChildMoniker {
pub fn new(name: String) -> ChildMoniker {
assert!(!name.is_empty());
ChildMoniker { name }
}
pub fn name(&self) -> &str {
&self.name
}
}
impl fmt::Display for ChildMoniker {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "#{}", self.name)
}
}
/// An absolute moniker describes the identity of a component instance in terms of its path
/// relative to the root of the component instance tree.
///
/// A root moniker is a moniker with an empty path.
///
/// Absolute monikers are only used internally within the component manager. Externally,
/// components are referenced by encoded relative moniker so as to minimize the amount of
/// information which is disclosed about the overall structure of the component instance tree.
///
/// Display notation: "/", "/name1", "/name1/name2", ...
#[derive(Eq, PartialEq, Debug)]
pub struct AbsoluteMoniker {
path: Vec<ChildMoniker>,
}
impl AbsoluteMoniker {
pub fn new(path: Vec<ChildMoniker>) -> AbsoluteMoniker {
AbsoluteMoniker { path }
}
pub fn path(&self) -> &Vec<ChildMoniker> {
&self.path
}
pub fn root() -> AbsoluteMoniker {
AbsoluteMoniker { path: vec![] }
}
pub fn is_root(&self) -> bool {
self.path.is_empty()
}
}
impl fmt::Display for AbsoluteMoniker {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.path.is_empty() {
write!(f, "/")?;
} else {
for segment in &self.path {
write!(f, "/{}", segment.name())?
}
}
Ok(())
}
}
/// A relative moniker describes the identity of a component instance in terms of its path
/// relative to another (unspecified) component in the component instance tree.
///
/// A self-reference moniker is a moniker with both empty "up" and "down" paths.
///
/// Relative monikers consist of two paths called "up" and "down".
/// - The "up" path describes a sequence of child-to-parent traversals heading towards the root of
/// the component instance tree.
/// - The "down" path describes a sequence of parent-to-child traversals heading towards a
/// different component instance in the tree.
///
/// These paths are minimal: no suffix segments of the "up" path can be a prefix segments of the
/// "down" path. All such common segments must be elided as part of canonicalizing the relative
/// moniker prior to construction.
///
/// Naming child monikers along both the "upwards" and "downwards" paths provides a strong
/// strong guarantee that relative monikers are only meaningful when interpreted within isomorphic
/// component instance subtrees. (Compare with relative filesystem path notations which use
/// ".." to perform upwards traversal and offer correspondingly weaker guarantees.)
///
/// For example, if two sibling component instances named "A" and "B" both possess relative
/// monikers for another component instance named "C", then A's moniker for C and B's moniker
/// for C will be distinct.
///
/// Display notation: ".", "./down1", ".\up1/down1", ".\up1\up2/down1", ...
#[derive(Eq, PartialEq, Debug)]
pub struct RelativeMoniker {
up_path: Vec<ChildMoniker>,
down_path: Vec<ChildMoniker>,
}
impl RelativeMoniker {
pub fn new(up_path: Vec<ChildMoniker>, down_path: Vec<ChildMoniker>) -> RelativeMoniker {
RelativeMoniker { up_path, down_path }
}
pub fn up_path(&self) -> &Vec<ChildMoniker> {
&self.up_path
}
pub fn down_path(&self) -> &Vec<ChildMoniker> {
&self.down_path
}
pub fn is_self(&self) -> bool {
self.up_path.is_empty() && self.down_path.is_empty()
}
}
impl fmt::Display for RelativeMoniker {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, ".")?;
for segment in &self.up_path {
write!(f, "\\{}", segment.name())?
}
for segment in &self.down_path {
write!(f, "/{}", segment.name())?
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn child_monikers() {
let m = ChildMoniker::new("test".to_string());
assert_eq!("test", m.name());
assert_eq!("#test", format!("{}", m));
}
#[test]
fn absolute_monikers() {
let root = AbsoluteMoniker::root();
assert_eq!(true, root.is_root());
assert_eq!("/", format!("{}", root));
let leaf = AbsoluteMoniker::new(vec![
ChildMoniker::new("a".to_string()),
ChildMoniker::new("b".to_string()),
]);
assert_eq!(false, leaf.is_root());
assert_eq!("/a/b", format!("{}", leaf));
}
#[test]
fn relative_monikers() {
let me = RelativeMoniker::new(vec![], vec![]);
assert_eq!(true, me.is_self());
assert_eq!(".", format!("{}", me));
let ancestor = RelativeMoniker::new(
vec![
ChildMoniker::new("a".to_string()),
ChildMoniker::new("b".to_string()),
],
vec![],
);
assert_eq!(false, ancestor.is_self());
assert_eq!(".\\a\\b", format!("{}", ancestor));
let descendant = RelativeMoniker::new(
vec![],
vec![
ChildMoniker::new("a".to_string()),
ChildMoniker::new("b".to_string()),
],
);
assert_eq!(false, descendant.is_self());
assert_eq!("./a/b", format!("{}", descendant));
let sibling = RelativeMoniker::new(
vec![ChildMoniker::new("a".to_string())],
vec![ChildMoniker::new("b".to_string())],
);
assert_eq!(false, sibling.is_self());
assert_eq!(".\\a/b", format!("{}", sibling));
let cousin = RelativeMoniker::new(
vec![
ChildMoniker::new("a".to_string()),
ChildMoniker::new("a0".to_string()),
],
vec![
ChildMoniker::new("b0".to_string()),
ChildMoniker::new("b".to_string()),
],
);
assert_eq!(false, cousin.is_self());
assert_eq!(".\\a\\a0/b0/b", format!("{}", cousin));
}
}