blob: f8117a29458077f9d433ef97112e496c4cb93b1f [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
crate::instanced_child_name::InstancedChildName,
core::cmp::{self, Ordering},
moniker::{ChildName, ChildNameBase, Moniker, MonikerBase, MonikerError},
std::{fmt, hash::Hash},
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// An instanced 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.
///
/// Instanced monikers are only used internally within the component manager. Externally,
/// components are referenced by encoded moniker so as to minimize the amount of
/// information which is disclosed about the overall structure of the component instance tree.
///
/// Display notation: ".", "name1:1", "name1:1/name2:2", ...
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Eq, PartialEq, Clone, Hash, Default)]
pub struct InstancedMoniker {
path: Vec<InstancedChildName>,
}
impl InstancedMoniker {
/// Create a new InstancedMoniker with zero-value InstanceIds for all path parts
/// in `moniker`.
pub fn from_moniker_with_zero_value_instance_ids(moniker: &Moniker) -> InstancedMoniker {
let path: Vec<InstancedChildName> =
moniker.path().iter().map(|p| InstancedChildName::from_child_moniker(p, 0)).collect();
InstancedMoniker::new(path)
}
/// Convert an InstancedMoniker into an allocated Moniker without InstanceIds
pub fn without_instance_ids(&self) -> Moniker {
let path: Vec<ChildName> = self.path().iter().map(|p| p.without_instance_id()).collect();
Moniker::new(path)
}
/// Transforms an `InstancedMoniker` into a representation where all dynamic children
/// have `0` value instance ids.
pub fn with_zero_value_instance_ids(&self) -> InstancedMoniker {
let path = self
.path()
.iter()
.map(|c| {
InstancedChildName::try_new(
c.name().as_str(),
c.collection().map(|c| c.as_str()),
0,
)
.expect("down path moniker is guaranteed to be valid")
})
.collect();
InstancedMoniker::new(path)
}
}
impl MonikerBase for InstancedMoniker {
type Part = InstancedChildName;
fn new(path: Vec<Self::Part>) -> Self {
Self { path }
}
fn path(&self) -> &Vec<Self::Part> {
&self.path
}
fn path_mut(&mut self) -> &mut Vec<Self::Part> {
&mut self.path
}
}
impl TryFrom<&str> for InstancedMoniker {
type Error = MonikerError;
fn try_from(input: &str) -> Result<Self, MonikerError> {
Self::parse_str(input)
}
}
impl TryFrom<Vec<&str>> for InstancedMoniker {
type Error = MonikerError;
fn try_from(rep: Vec<&str>) -> Result<Self, MonikerError> {
Self::parse(&rep)
}
}
impl cmp::Ord for InstancedMoniker {
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.compare(other)
}
}
impl PartialOrd for InstancedMoniker {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for InstancedMoniker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.format(f)
}
}
impl fmt::Debug for InstancedMoniker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.format(f)
}
}
#[cfg(test)]
mod tests {
use std::str::FromStr;
use {super::*, cm_types::Name};
#[test]
fn from_moniker() {
let m = Moniker::from_str("foo/bar").unwrap();
let instanced = InstancedMoniker::from_moniker_with_zero_value_instance_ids(&m);
assert_eq!(instanced.to_string(), "foo:0/bar:0");
}
#[test]
fn instanced_monikers() {
let root = InstancedMoniker::root();
assert_eq!(true, root.is_root());
assert_eq!(".", format!("{}", root));
assert_eq!(root, InstancedMoniker::try_from(vec![]).unwrap());
let m = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", Some("coll"), 2).unwrap(),
]);
assert_eq!(false, m.is_root());
assert_eq!("a:1/coll:b:2", format!("{}", m));
assert_eq!(m, InstancedMoniker::try_from(vec!["a:1", "coll:b:2"]).unwrap());
assert_eq!(m.leaf().map(|m| m.collection()).flatten(), Some(&Name::new("coll").unwrap()));
assert_eq!(m.leaf().map(|m| m.name().as_str()), Some("b"));
assert_eq!(m.leaf().map(|m| m.instance()), Some(2));
assert_eq!(m.leaf(), Some(&InstancedChildName::try_from("coll:b:2").unwrap()));
}
#[test]
fn instanced_moniker_parent() {
let root = InstancedMoniker::root();
assert_eq!(true, root.is_root());
assert_eq!(None, root.parent());
let m = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
]);
assert_eq!("a:1/b:2", format!("{}", m));
assert_eq!("a:1", format!("{}", m.parent().unwrap()));
assert_eq!(".", format!("{}", m.parent().unwrap().parent().unwrap()));
assert_eq!(None, m.parent().unwrap().parent().unwrap().parent());
assert_eq!(m.leaf(), Some(&InstancedChildName::try_from("b:2").unwrap()));
}
#[test]
fn instanced_moniker_compare() {
let a = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
InstancedChildName::try_new("c", None, 3).unwrap(),
]);
let a2 = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 3).unwrap(),
InstancedChildName::try_new("c", None, 3).unwrap(),
]);
let b = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
InstancedChildName::try_new("b", None, 3).unwrap(),
]);
let c = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
InstancedChildName::try_new("c", None, 3).unwrap(),
InstancedChildName::try_new("d", None, 4).unwrap(),
]);
let d = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
InstancedChildName::try_new("c", None, 3).unwrap(),
]);
assert_eq!(Ordering::Less, a.cmp(&a2));
assert_eq!(Ordering::Greater, a2.cmp(&a));
assert_eq!(Ordering::Greater, a.cmp(&b));
assert_eq!(Ordering::Less, b.cmp(&a));
assert_eq!(Ordering::Less, a.cmp(&c));
assert_eq!(Ordering::Greater, c.cmp(&a));
assert_eq!(Ordering::Equal, a.cmp(&d));
assert_eq!(Ordering::Equal, d.cmp(&a));
assert_eq!(Ordering::Less, b.cmp(&c));
assert_eq!(Ordering::Greater, c.cmp(&b));
assert_eq!(Ordering::Less, b.cmp(&d));
assert_eq!(Ordering::Greater, d.cmp(&b));
assert_eq!(Ordering::Greater, c.cmp(&d));
assert_eq!(Ordering::Less, d.cmp(&c));
}
#[test]
fn instanced_monikers_has_prefix() {
let root = InstancedMoniker::root();
let a = InstancedMoniker::new(vec![InstancedChildName::try_new("a", None, 1).unwrap()]);
let ab = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
]);
let abc = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
InstancedChildName::try_new("c", None, 3).unwrap(),
]);
let abd = InstancedMoniker::new(vec![
InstancedChildName::try_new("a", None, 1).unwrap(),
InstancedChildName::try_new("b", None, 2).unwrap(),
InstancedChildName::try_new("d", None, 3).unwrap(),
]);
assert!(root.has_prefix(&root));
assert!(a.has_prefix(&root));
assert!(ab.has_prefix(&root));
assert!(abc.has_prefix(&root));
assert!(abd.has_prefix(&root));
assert!(!root.has_prefix(&a));
assert!(a.has_prefix(&a));
assert!(ab.has_prefix(&a));
assert!(abc.has_prefix(&a));
assert!(abd.has_prefix(&a));
assert!(!root.has_prefix(&ab));
assert!(!a.has_prefix(&ab));
assert!(ab.has_prefix(&ab));
assert!(abc.has_prefix(&ab));
assert!(abd.has_prefix(&ab));
assert!(!root.has_prefix(&abc));
assert!(abc.has_prefix(&abc));
assert!(!a.has_prefix(&abc));
assert!(!ab.has_prefix(&abc));
assert!(!abd.has_prefix(&abc));
assert!(!abd.has_prefix(&abc));
assert!(abd.has_prefix(&abd));
assert!(!a.has_prefix(&abd));
assert!(!ab.has_prefix(&abd));
assert!(!abc.has_prefix(&abd));
}
#[test]
fn instanced_moniker_parse_str() -> Result<(), MonikerError> {
let under_test = |s| InstancedMoniker::parse_str(s);
assert_eq!(under_test(".")?, InstancedMoniker::root());
let a = InstancedChildName::try_new("a", None, 0).unwrap();
let bb = InstancedChildName::try_new("b", Some("b"), 0).unwrap();
assert_eq!(under_test("a:0")?, InstancedMoniker::new(vec![a.clone()]));
assert_eq!(under_test("a:0/b:b:0")?, InstancedMoniker::new(vec![a.clone(), bb.clone()]));
assert_eq!(
under_test("a:0/b:b:0/a:0/b:b:0")?,
InstancedMoniker::new(vec![a.clone(), bb.clone(), a.clone(), bb.clone()])
);
assert!(under_test("").is_err(), "cannot be empty");
assert!(under_test("a:0/").is_err(), "path segments cannot be empty");
assert!(under_test("a:0//b:0").is_err(), "path segments cannot be empty");
assert!(under_test("a:a").is_err(), "must contain instance id");
Ok(())
}
}