Skip to main content

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