blob: f5bbd3fe23b7359a30bad85c03719732fe674c3d [file] [log] [blame]
// Copyright 2021 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::child_name::ChildName;
use crate::error::MonikerError;
use core::cmp::{self, Ordering, PartialEq};
use std::fmt;
use std::hash::Hash;
/// [Moniker] describes the identity of a component instance in terms of its path relative to the
/// root of the component instance tree.
///
/// Display notation: ".", "name1", "name1/name2", ...
#[derive(Eq, PartialEq, Clone, Hash, Default)]
pub struct Moniker {
path: Vec<ChildName>,
}
impl Moniker {
pub fn new(path: Vec<ChildName>) -> Self {
Self { path }
}
pub fn path(&self) -> &Vec<ChildName> {
&self.path
}
pub fn path_mut(&mut self) -> &mut Vec<ChildName> {
&mut self.path
}
pub fn parse<T: AsRef<str>>(path: &[T]) -> Result<Self, MonikerError> {
let path: Result<Vec<ChildName>, MonikerError> =
path.iter().map(ChildName::parse).collect();
Ok(Self::new(path?))
}
pub fn parse_str(input: &str) -> Result<Self, MonikerError> {
if input.is_empty() {
return Err(MonikerError::invalid_moniker(input));
}
if input == "/" || input == "." || input == "./" {
return Ok(Self::new(vec![]));
}
// Optionally strip a prefix of "/" or "./".
let stripped = match input.strip_prefix("/") {
Some(s) => s,
None => match input.strip_prefix("./") {
Some(s) => s,
None => input,
},
};
let path =
stripped.split('/').map(ChildName::parse).collect::<Result<_, MonikerError>>()?;
Ok(Self::new(path))
}
/// Concatenates other onto the end of this moniker.
pub fn concat(&self, other: &Moniker) -> Self {
let mut path = self.path().clone();
let mut other_path = other.path().clone();
path.append(&mut other_path);
Self::new(path)
}
/// Indicates whether this moniker is prefixed by prefix.
pub fn has_prefix(&self, prefix: &Moniker) -> bool {
if self.path().len() < prefix.path().len() {
return false;
}
prefix.path().iter().enumerate().all(|item| *item.1 == self.path()[item.0])
}
pub fn root() -> Self {
Self::new(vec![])
}
pub fn leaf(&self) -> Option<&ChildName> {
self.path().last()
}
pub fn is_root(&self) -> bool {
self.path().is_empty()
}
pub fn parent(&self) -> Option<Self> {
if self.is_root() {
None
} else {
let l = self.path().len() - 1;
Some(Self::new(self.path()[..l].to_vec()))
}
}
pub fn child(&self, child: ChildName) -> Self {
let mut path = self.path().clone();
path.push(child);
Self::new(path)
}
pub fn next(&mut self) -> Option<ChildName> {
let path = self.path_mut();
if path.is_empty() {
return None;
}
Some(path.remove(0))
}
/// Strips the moniker parts in prefix from the beginning of this moniker.
pub fn strip_prefix(&self, prefix: &Moniker) -> Result<Self, MonikerError> {
if !self.has_prefix(prefix) {
return Err(MonikerError::MonikerDoesNotHavePrefix {
moniker: self.to_string(),
prefix: prefix.to_string(),
});
}
let prefix_len = prefix.path().len();
let mut path = self.path().clone();
path.drain(0..prefix_len);
Ok(Self::new(path))
}
}
impl TryFrom<Vec<&str>> for Moniker {
type Error = MonikerError;
fn try_from(rep: Vec<&str>) -> Result<Self, MonikerError> {
Self::parse(&rep)
}
}
impl TryFrom<&str> for Moniker {
type Error = MonikerError;
fn try_from(input: &str) -> Result<Self, MonikerError> {
Self::parse_str(input)
}
}
impl std::str::FromStr for Moniker {
type Err = MonikerError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse_str(s)
}
}
impl cmp::Ord for Moniker {
fn cmp(&self, other: &Self) -> cmp::Ordering {
let min_size = cmp::min(self.path().len(), other.path().len());
for i in 0..min_size {
if self.path()[i] < other.path()[i] {
return cmp::Ordering::Less;
} else if self.path()[i] > other.path()[i] {
return cmp::Ordering::Greater;
}
}
if self.path().len() > other.path().len() {
return cmp::Ordering::Greater;
} else if self.path().len() < other.path().len() {
return cmp::Ordering::Less;
}
cmp::Ordering::Equal
}
}
impl PartialOrd for Moniker {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for Moniker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.path().is_empty() {
write!(f, ".")?;
} else {
write!(f, "{}", self.path()[0])?;
for segment in self.path()[1..].iter() {
write!(f, "/{}", segment)?;
}
}
Ok(())
}
}
impl fmt::Debug for Moniker {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{self}")
}
}
#[cfg(test)]
mod tests {
use super::*;
use cm_types::Name;
#[test]
fn monikers() {
let root = Moniker::root();
assert_eq!(true, root.is_root());
assert_eq!(".", format!("{}", root));
assert_eq!(root, Moniker::new(vec![]));
assert_eq!(root, Moniker::try_from(vec![]).unwrap());
let m = Moniker::new(vec![
ChildName::try_new("a", None).unwrap(),
ChildName::try_new("b", Some("coll")).unwrap(),
]);
assert_eq!(false, m.is_root());
assert_eq!("a/coll:b", format!("{}", m));
assert_eq!(m, Moniker::try_from(vec!["a", "coll:b"]).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(), Some(&ChildName::try_from("coll:b").unwrap()));
}
#[test]
fn moniker_parent() {
let root = Moniker::root();
assert_eq!(true, root.is_root());
assert_eq!(None, root.parent());
let m = Moniker::new(vec![
ChildName::try_new("a", None).unwrap(),
ChildName::try_new("b", None).unwrap(),
]);
assert_eq!("a/b", format!("{}", m));
assert_eq!("a", 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(&ChildName::try_from("b").unwrap()));
}
#[test]
fn moniker_concat() {
let scope_root: Moniker = vec!["a:test1", "b:test2"].try_into().unwrap();
let relative: Moniker = vec!["c:test3", "d:test4"].try_into().unwrap();
let descendant = scope_root.concat(&relative);
assert_eq!("a:test1/b:test2/c:test3/d:test4", format!("{}", descendant));
let relative: Moniker = vec![].try_into().unwrap();
let descendant = scope_root.concat(&relative);
assert_eq!("a:test1/b:test2", format!("{}", descendant));
}
#[test]
fn moniker_next() {
let mut root = Moniker::root();
assert_eq!(None, root.next());
let mut m = Moniker::new(vec![
ChildName::try_new("a", None).unwrap(),
ChildName::try_new("b", None).unwrap(),
]);
assert_eq!("a", format!("{}", m.next().unwrap()));
assert_eq!("b", format!("{}", m.next().unwrap()));
assert_eq!(None, m.next());
assert_eq!(None, m.next());
}
#[test]
fn moniker_parse_str() {
assert_eq!(Moniker::try_from("/foo").unwrap(), Moniker::try_from(vec!["foo"]).unwrap());
assert_eq!(Moniker::try_from("./foo").unwrap(), Moniker::try_from(vec!["foo"]).unwrap());
assert_eq!(Moniker::try_from("foo").unwrap(), Moniker::try_from(vec!["foo"]).unwrap());
assert_eq!(Moniker::try_from("/").unwrap(), Moniker::try_from(vec![]).unwrap());
assert_eq!(Moniker::try_from("./").unwrap(), Moniker::try_from(vec![]).unwrap());
assert!(Moniker::try_from("//foo").is_err());
assert!(Moniker::try_from(".//foo").is_err());
assert!(Moniker::try_from("/./foo").is_err());
assert!(Moniker::try_from("../foo").is_err());
assert!(Moniker::try_from(".foo").is_err());
}
}