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::{BorrowedLongName, BorrowedName, LongName, Name};
7use flyweights::FlyStr;
8use std::borrow::Borrow;
9use std::cmp::Ordering;
10use std::fmt;
11use std::hash::{Hash, Hasher};
12use std::str::FromStr;
13
14/// A [ChildName] locally identifies a child component instance using the name assigned by
15/// its parent and its collection (if present). It is the building block of [Moniker].
16///
17/// Display notation: "[collection:]name".
18#[derive(PartialEq, Eq, Clone)]
19pub struct ChildName {
20    rep: FlyStr,
21}
22
23impl ChildName {
24    pub fn new(name: LongName, collection: Option<Name>) -> Self {
25        match collection {
26            Some(collection) => Self { rep: format!("{collection}:{name}").into() },
27            None => Self { rep: format!("{name}").into() },
28        }
29    }
30
31    pub fn try_new<S>(name: S, collection: Option<S>) -> Result<Self, MonikerError>
32    where
33        S: AsRef<str> + Into<String>,
34    {
35        let name = name.as_ref();
36        let rep = match collection {
37            Some(collection) => Self { rep: format!("{}:{name}", collection.as_ref()).into() },
38            None => Self { rep: format!("{name}").into() },
39        };
40        Self::parse(rep)
41    }
42
43    /// Parses a `ChildName` from a string.
44    ///
45    /// Input strings should be of the format `[collection:]name`, e.g. `foo` or `biz:foo`.
46    pub fn parse<T: AsRef<str>>(rep: T) -> Result<Self, MonikerError> {
47        validate_child_name(rep.as_ref())?;
48        Ok(Self { rep: rep.as_ref().into() })
49    }
50
51    pub fn name(&self) -> &BorrowedLongName {
52        match self.rep.find(':') {
53            Some(i) => {
54                BorrowedLongName::new(&self.rep[i + 1..]).expect("name guaranteed to be valid")
55            }
56            None => BorrowedLongName::new(&self.rep).expect("name guaranteed to be valid"),
57        }
58    }
59
60    pub fn collection(&self) -> Option<&BorrowedName> {
61        self.rep
62            .find(':')
63            .map(|i| BorrowedName::new(&self.rep[0..i]).expect("collection guaranteed to be valid"))
64    }
65}
66
67impl TryFrom<&str> for ChildName {
68    type Error = MonikerError;
69
70    #[inline]
71    fn try_from(rep: &str) -> Result<Self, Self::Error> {
72        Self::parse(rep)
73    }
74}
75
76impl FromStr for ChildName {
77    type Err = MonikerError;
78
79    #[inline]
80    fn from_str(rep: &str) -> Result<Self, Self::Err> {
81        Self::parse(rep)
82    }
83}
84
85impl From<cm_rust::ChildRef> for ChildName {
86    fn from(child_ref: cm_rust::ChildRef) -> Self {
87        Self::new(child_ref.name, child_ref.collection)
88    }
89}
90
91impl From<&BorrowedChildName> for ChildName {
92    fn from(o: &BorrowedChildName) -> Self {
93        Self { rep: o.rep.into() }
94    }
95}
96
97impl From<ChildName> for cm_rust::ChildRef {
98    fn from(child_name: ChildName) -> Self {
99        Self { name: child_name.name().into(), collection: child_name.collection().map(Into::into) }
100    }
101}
102
103impl AsRef<str> for ChildName {
104    #[inline]
105    fn as_ref(&self) -> &str {
106        self.rep.as_str()
107    }
108}
109
110impl AsRef<BorrowedChildName> for ChildName {
111    #[inline]
112    fn as_ref(&self) -> &BorrowedChildName {
113        BorrowedChildName::new_unchecked(self)
114    }
115}
116
117impl Borrow<BorrowedChildName> for ChildName {
118    #[inline]
119    fn borrow(&self) -> &BorrowedChildName {
120        &BorrowedChildName::new_unchecked(self)
121    }
122}
123
124impl Borrow<str> for ChildName {
125    #[inline]
126    fn borrow(&self) -> &str {
127        &self.rep
128    }
129}
130
131impl std::ops::Deref for ChildName {
132    type Target = BorrowedChildName;
133
134    #[inline]
135    fn deref(&self) -> &BorrowedChildName {
136        BorrowedChildName::new_unchecked(self.rep.as_str())
137    }
138}
139
140impl PartialEq<&str> for ChildName {
141    #[inline]
142    fn eq(&self, o: &&str) -> bool {
143        &*self.rep == *o
144    }
145}
146
147impl PartialEq<String> for ChildName {
148    #[inline]
149    fn eq(&self, o: &String) -> bool {
150        &*self.rep == *o
151    }
152}
153
154impl PartialEq<BorrowedChildName> for ChildName {
155    #[inline]
156    fn eq(&self, o: &BorrowedChildName) -> bool {
157        &self.rep == &o.rep
158    }
159}
160
161impl Ord for ChildName {
162    #[inline]
163    fn cmp(&self, other: &Self) -> Ordering {
164        (self.collection(), self.name()).cmp(&(other.collection(), other.name()))
165    }
166}
167
168impl PartialOrd for ChildName {
169    #[inline]
170    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
171        Some(self.cmp(other))
172    }
173}
174
175impl Hash for ChildName {
176    #[inline]
177    fn hash<H: Hasher>(&self, state: &mut H) {
178        self.rep.as_str().hash(state)
179    }
180}
181
182impl fmt::Display for ChildName {
183    #[inline]
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        write!(f, "{}", self.rep)
186    }
187}
188
189impl fmt::Debug for ChildName {
190    #[inline]
191    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
192        write!(f, "{self}")
193    }
194}
195
196/// Like [`ChildName`], except it holds a string slice rather than an allocated string. For
197/// example, the [`Moniker`] API uses this to return path segments without making an allocation.
198#[derive(Eq, PartialEq)]
199#[repr(transparent)]
200pub struct BorrowedChildName {
201    rep: str,
202}
203
204impl BorrowedChildName {
205    /// Parses a `ChildName` from a string.
206    ///
207    /// Input strings should be of the format `[collection:]name`, e.g. `foo` or `biz:foo`.
208    pub fn parse<S: AsRef<str> + ?Sized>(s: &S) -> Result<&Self, MonikerError> {
209        validate_child_name(s.as_ref())?;
210        Ok(Self::new_unchecked(s))
211    }
212
213    /// Private variant of [`BorrowedChildName::parse`] that does not perform correctness checks.
214    /// For efficiency when the caller is sure `s` is a valid [`ChildName`].
215    pub(crate) fn new_unchecked<S: AsRef<str> + ?Sized>(s: &S) -> &Self {
216        unsafe { &*(s.as_ref() as *const str as *const Self) }
217    }
218
219    pub fn name(&self) -> &BorrowedLongName {
220        match self.rep.find(':') {
221            Some(i) => {
222                BorrowedLongName::new(&self.rep[i + 1..]).expect("name guaranteed to be valid")
223            }
224            None => BorrowedLongName::new(&self.rep).expect("name guaranteed to be valid"),
225        }
226    }
227
228    pub fn collection(&self) -> Option<&BorrowedName> {
229        self.rep
230            .find(':')
231            .map(|i| BorrowedName::new(&self.rep[0..i]).expect("collection guaranteed to be valid"))
232    }
233}
234
235impl From<&BorrowedChildName> for cm_rust::ChildRef {
236    fn from(child_name: &BorrowedChildName) -> Self {
237        Self { name: child_name.name().into(), collection: child_name.collection().map(Into::into) }
238    }
239}
240
241impl AsRef<str> for BorrowedChildName {
242    #[inline]
243    fn as_ref(&self) -> &str {
244        &self.rep
245    }
246}
247
248impl Borrow<str> for BorrowedChildName {
249    #[inline]
250    fn borrow(&self) -> &str {
251        &self.rep
252    }
253}
254
255impl Borrow<str> for &BorrowedChildName {
256    #[inline]
257    fn borrow(&self) -> &str {
258        &self.rep
259    }
260}
261
262impl PartialEq<&str> for BorrowedChildName {
263    #[inline]
264    fn eq(&self, o: &&str) -> bool {
265        &self.rep == *o
266    }
267}
268
269impl PartialEq<String> for BorrowedChildName {
270    #[inline]
271    fn eq(&self, o: &String) -> bool {
272        &self.rep == &*o
273    }
274}
275
276impl PartialEq<ChildName> for BorrowedChildName {
277    #[inline]
278    fn eq(&self, o: &ChildName) -> bool {
279        self.rep == *o.rep
280    }
281}
282
283impl Ord for BorrowedChildName {
284    #[inline]
285    fn cmp(&self, other: &Self) -> Ordering {
286        (self.collection(), self.name()).cmp(&(other.collection(), other.name()))
287    }
288}
289
290impl PartialOrd for BorrowedChildName {
291    #[inline]
292    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
293        Some(self.cmp(other))
294    }
295}
296
297impl Hash for BorrowedChildName {
298    #[inline]
299    fn hash<H: Hasher>(&self, state: &mut H) {
300        self.rep.hash(state)
301    }
302}
303
304impl fmt::Display for BorrowedChildName {
305    #[inline]
306    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307        write!(f, "{}", &self.rep)
308    }
309}
310
311impl fmt::Debug for BorrowedChildName {
312    #[inline]
313    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314        write!(f, "{self}")
315    }
316}
317
318fn validate_child_name(name: &str) -> Result<(), MonikerError> {
319    match name.find(':') {
320        Some(i) => {
321            let collection = &name[0..i];
322            let name = &name[i + 1..];
323            let _ = BorrowedName::new(collection)?;
324            let _ = BorrowedLongName::new(name)?;
325        }
326        None => {
327            let _ = BorrowedLongName::new(name)?;
328        }
329    }
330    Ok(())
331}
332
333#[cfg(test)]
334mod tests {
335    use super::*;
336    use cm_types::{MAX_LONG_NAME_LENGTH, MAX_NAME_LENGTH};
337    use std::collections::HashSet;
338    use std::iter::repeat;
339
340    #[test]
341    fn child_monikers() {
342        let m = ChildName::try_new("test", None).unwrap();
343        assert_eq!("test", m.name().as_str());
344        assert_eq!(None, m.collection());
345        assert_eq!("test", format!("{}", m));
346        assert_eq!(m, ChildName::try_from("test").unwrap());
347
348        let m = ChildName::try_new("test", Some("coll")).unwrap();
349        assert_eq!("test", m.name().as_str());
350        assert_eq!(Some(BorrowedName::new("coll").unwrap()), m.collection());
351        assert_eq!("coll:test", format!("{}", m));
352        assert_eq!(m, ChildName::parse("coll:test").unwrap());
353
354        let max_coll_length_part = "f".repeat(MAX_NAME_LENGTH);
355        let max_name_length_part = "f".repeat(MAX_LONG_NAME_LENGTH);
356        let max_moniker_length = format!("{}:{}", max_coll_length_part, max_name_length_part);
357        let m = ChildName::parse(max_moniker_length).expect("valid moniker");
358        assert_eq!(&max_name_length_part, m.name().as_str());
359        assert_eq!(Some(BorrowedName::new(&max_coll_length_part).unwrap()), m.collection());
360
361        assert!(ChildName::parse("").is_err(), "cannot be empty");
362        assert!(ChildName::parse(":").is_err(), "cannot be empty with colon");
363        assert!(ChildName::parse("f:").is_err(), "second part cannot be empty with colon");
364        assert!(ChildName::parse(":f").is_err(), "first part cannot be empty with colon");
365        assert!(ChildName::parse("f:f:f").is_err(), "multiple colons not allowed");
366        assert!(ChildName::parse("@").is_err(), "invalid character in name");
367        assert!(ChildName::parse("@:f").is_err(), "invalid character in collection");
368        assert!(ChildName::parse("f:@").is_err(), "invalid character in name with collection");
369        assert!(
370            ChildName::parse(&format!("f:{}", "x".repeat(MAX_LONG_NAME_LENGTH + 1))).is_err(),
371            "name too long"
372        );
373        assert!(
374            ChildName::parse(&format!("{}:x", "f".repeat(MAX_NAME_LENGTH + 1))).is_err(),
375            "collection too long"
376        );
377    }
378
379    #[test]
380    fn child_moniker_compare() {
381        let a = ChildName::try_new("a", None).unwrap();
382        let aa = ChildName::try_new("a", Some("a")).unwrap();
383        let ab = ChildName::try_new("a", Some("b")).unwrap();
384        let ba = ChildName::try_new("b", Some("a")).unwrap();
385        let bb = ChildName::try_new("b", Some("b")).unwrap();
386        let aa_same = ChildName::try_new("a", Some("a")).unwrap();
387
388        assert_eq!(Ordering::Less, a.cmp(&aa));
389        assert_eq!(Ordering::Greater, aa.cmp(&a));
390        assert_eq!(Ordering::Less, a.cmp(&ab));
391        assert_eq!(Ordering::Greater, ab.cmp(&a));
392        assert_eq!(Ordering::Less, a.cmp(&ba));
393        assert_eq!(Ordering::Greater, ba.cmp(&a));
394        assert_eq!(Ordering::Less, a.cmp(&bb));
395        assert_eq!(Ordering::Greater, bb.cmp(&a));
396
397        assert_eq!(Ordering::Less, aa.cmp(&ab));
398        assert_eq!(Ordering::Greater, ab.cmp(&aa));
399        assert_eq!(Ordering::Less, aa.cmp(&ba));
400        assert_eq!(Ordering::Greater, ba.cmp(&aa));
401        assert_eq!(Ordering::Less, aa.cmp(&bb));
402        assert_eq!(Ordering::Greater, bb.cmp(&aa));
403        assert_eq!(Ordering::Equal, aa.cmp(&aa_same));
404        assert_eq!(Ordering::Equal, aa_same.cmp(&aa));
405
406        assert_eq!(Ordering::Greater, ab.cmp(&ba));
407        assert_eq!(Ordering::Less, ba.cmp(&ab));
408        assert_eq!(Ordering::Less, ab.cmp(&bb));
409        assert_eq!(Ordering::Greater, bb.cmp(&ab));
410
411        assert_eq!(Ordering::Less, ba.cmp(&bb));
412        assert_eq!(Ordering::Greater, bb.cmp(&ba));
413    }
414
415    #[test]
416    fn hash() {
417        {
418            let n1 = ChildName::new("a".parse().unwrap(), None);
419            let s_b = repeat("b").take(1024).collect::<String>();
420            let n2 = ChildName::new(s_b.parse().unwrap(), None);
421            let b1 = BorrowedChildName::parse("a").unwrap();
422            let b2 = BorrowedChildName::parse(&s_b).unwrap();
423
424            let mut set = HashSet::new();
425            set.insert(n1.clone());
426            assert!(set.contains(&n1));
427            assert!(set.contains(b1));
428            assert!(!set.contains(&n2));
429            assert!(!set.contains(b2));
430            set.insert(n2.clone());
431            assert!(set.contains(&n1));
432            assert!(set.contains(b1));
433            assert!(set.contains(&n2));
434            assert!(set.contains(b2));
435        }
436        {
437            let n1 = ChildName::new("a".parse().unwrap(), Some("c".parse().unwrap()));
438            let s_b = repeat("b").take(1024).collect::<String>();
439            let s_c = repeat("c").take(255).collect::<String>();
440            let n2 = ChildName::new(s_b.parse().unwrap(), Some(s_c.parse().unwrap()));
441            let b1 = BorrowedChildName::parse("c:a").unwrap();
442            let s_c_b = &format!("{s_c}:{s_b}");
443            let b2 = BorrowedChildName::parse(&s_c_b).unwrap();
444
445            let mut set = HashSet::new();
446            set.insert(n1.clone());
447            assert!(set.contains(&n1));
448            assert!(set.contains(b1));
449            assert!(!set.contains(&n2));
450            assert!(!set.contains(b2));
451            set.insert(n2.clone());
452            assert!(set.contains(&n1));
453            assert!(set.contains(b1));
454            assert!(set.contains(&n2));
455            assert!(set.contains(b2));
456        }
457    }
458}