moniker/
child_name.rs

1// Copyright 2021 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::error::MonikerError;
6use cm_types::{LongName, Name};
7use core::cmp::Ordering;
8use std::fmt;
9
10/// A [ChildName] locally identifies a child component instance using the name assigned by
11/// its parent and its collection (if present). It is the building block of [Moniker].
12///
13/// Display notation: "[collection:]name".
14#[derive(Eq, PartialEq, Clone, Hash)]
15pub struct ChildName {
16    pub name: LongName,
17    pub collection: Option<Name>,
18}
19
20impl ChildName {
21    pub fn new(name: LongName, collection: Option<Name>) -> Self {
22        Self { name, collection }
23    }
24
25    pub fn try_new<S>(name: S, collection: Option<S>) -> Result<Self, MonikerError>
26    where
27        S: AsRef<str> + Into<String>,
28    {
29        let name = LongName::new(name)?;
30        let collection = match collection {
31            Some(coll) => {
32                let coll_name = Name::new(coll)?;
33                Some(coll_name)
34            }
35            None => None,
36        };
37        Ok(Self { name, collection })
38    }
39
40    /// Parses a `ChildName` from a string.
41    ///
42    /// Input strings should be of the format `[collection:]name`, e.g. `foo` or `biz:foo`.
43    pub fn parse<T: AsRef<str>>(rep: T) -> Result<Self, MonikerError> {
44        let rep = rep.as_ref();
45        let parts: Vec<&str> = rep.split(":").collect();
46        let (coll, name) = match parts.len() {
47            1 => (None, parts[0]),
48            2 => (Some(parts[0]), parts[1]),
49            _ => return Err(MonikerError::invalid_moniker(rep)),
50        };
51        ChildName::try_new(name, coll)
52    }
53
54    pub fn name(&self) -> &LongName {
55        &self.name
56    }
57
58    pub fn collection(&self) -> Option<&Name> {
59        self.collection.as_ref()
60    }
61}
62
63impl TryFrom<&str> for ChildName {
64    type Error = MonikerError;
65
66    fn try_from(rep: &str) -> Result<Self, MonikerError> {
67        ChildName::parse(rep)
68    }
69}
70
71impl From<cm_rust::ChildRef> for ChildName {
72    fn from(child_ref: cm_rust::ChildRef) -> Self {
73        Self { name: child_ref.name, collection: child_ref.collection }
74    }
75}
76
77impl From<ChildName> for cm_rust::ChildRef {
78    fn from(child_name: ChildName) -> Self {
79        Self { name: child_name.name, collection: child_name.collection }
80    }
81}
82
83impl Ord for ChildName {
84    fn cmp(&self, other: &Self) -> Ordering {
85        (&self.collection, &self.name).cmp(&(&other.collection, &other.name))
86    }
87}
88
89impl PartialOrd for ChildName {
90    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
91        Some(self.cmp(other))
92    }
93}
94
95impl fmt::Display for ChildName {
96    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97        if let Some(coll) = &self.collection {
98            write!(f, "{}:{}", coll, self.name)
99        } else {
100            write!(f, "{}", self.name)
101        }
102    }
103}
104
105impl fmt::Debug for ChildName {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        write!(f, "{self}")
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114    use cm_types::{MAX_LONG_NAME_LENGTH, MAX_NAME_LENGTH};
115
116    #[test]
117    fn child_monikers() {
118        let m = ChildName::try_new("test", None).unwrap();
119        assert_eq!("test", m.name().as_str());
120        assert_eq!(None, m.collection());
121        assert_eq!("test", format!("{}", m));
122        assert_eq!(m, ChildName::try_from("test").unwrap());
123
124        let m = ChildName::try_new("test", Some("coll")).unwrap();
125        assert_eq!("test", m.name().as_str());
126        assert_eq!(Some(&Name::new("coll").unwrap()), m.collection());
127        assert_eq!("coll:test", format!("{}", m));
128        assert_eq!(m, ChildName::try_from("coll:test").unwrap());
129
130        let max_coll_length_part = "f".repeat(MAX_NAME_LENGTH);
131        let max_name_length_part = "f".repeat(MAX_LONG_NAME_LENGTH);
132        let max_moniker_length = format!("{}:{}", max_coll_length_part, max_name_length_part);
133        let m = ChildName::parse(max_moniker_length).expect("valid moniker");
134        assert_eq!(&max_name_length_part, m.name().as_str());
135        assert_eq!(Some(&Name::new(max_coll_length_part).unwrap()), m.collection());
136
137        assert!(ChildName::parse("").is_err(), "cannot be empty");
138        assert!(ChildName::parse(":").is_err(), "cannot be empty with colon");
139        assert!(ChildName::parse("f:").is_err(), "second part cannot be empty with colon");
140        assert!(ChildName::parse(":f").is_err(), "first part cannot be empty with colon");
141        assert!(ChildName::parse("f:f:f").is_err(), "multiple colons not allowed");
142        assert!(ChildName::parse("@").is_err(), "invalid character in name");
143        assert!(ChildName::parse("@:f").is_err(), "invalid character in collection");
144        assert!(ChildName::parse("f:@").is_err(), "invalid character in name with collection");
145        assert!(
146            ChildName::parse(&format!("f:{}", "x".repeat(MAX_LONG_NAME_LENGTH + 1))).is_err(),
147            "name too long"
148        );
149        assert!(
150            ChildName::parse(&format!("{}:x", "f".repeat(MAX_NAME_LENGTH + 1))).is_err(),
151            "collection too long"
152        );
153    }
154
155    #[test]
156    fn child_moniker_compare() {
157        let a = ChildName::try_new("a", None).unwrap();
158        let aa = ChildName::try_new("a", Some("a")).unwrap();
159        let ab = ChildName::try_new("a", Some("b")).unwrap();
160        let ba = ChildName::try_new("b", Some("a")).unwrap();
161        let bb = ChildName::try_new("b", Some("b")).unwrap();
162        let aa_same = ChildName::try_new("a", Some("a")).unwrap();
163
164        assert_eq!(Ordering::Less, a.cmp(&aa));
165        assert_eq!(Ordering::Greater, aa.cmp(&a));
166        assert_eq!(Ordering::Less, a.cmp(&ab));
167        assert_eq!(Ordering::Greater, ab.cmp(&a));
168        assert_eq!(Ordering::Less, a.cmp(&ba));
169        assert_eq!(Ordering::Greater, ba.cmp(&a));
170        assert_eq!(Ordering::Less, a.cmp(&bb));
171        assert_eq!(Ordering::Greater, bb.cmp(&a));
172
173        assert_eq!(Ordering::Less, aa.cmp(&ab));
174        assert_eq!(Ordering::Greater, ab.cmp(&aa));
175        assert_eq!(Ordering::Less, aa.cmp(&ba));
176        assert_eq!(Ordering::Greater, ba.cmp(&aa));
177        assert_eq!(Ordering::Less, aa.cmp(&bb));
178        assert_eq!(Ordering::Greater, bb.cmp(&aa));
179        assert_eq!(Ordering::Equal, aa.cmp(&aa_same));
180        assert_eq!(Ordering::Equal, aa_same.cmp(&aa));
181
182        assert_eq!(Ordering::Greater, ab.cmp(&ba));
183        assert_eq!(Ordering::Less, ba.cmp(&ab));
184        assert_eq!(Ordering::Less, ab.cmp(&bb));
185        assert_eq!(Ordering::Greater, bb.cmp(&ab));
186
187        assert_eq!(Ordering::Less, ba.cmp(&bb));
188        assert_eq!(Ordering::Greater, bb.cmp(&ba));
189    }
190}