moniker/
moniker.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::child_name::{BorrowedChildName, ChildName};
6use crate::error::MonikerError;
7use cm_rust::{FidlIntoNative, NativeIntoFidl};
8use core::cmp::{self, Ordering, PartialEq};
9use flyweights::FlyStr;
10use std::fmt;
11use std::hash::Hash;
12
13/// [Moniker] describes the identity of a component instance in terms of its path relative to the
14/// root of the component instance tree.
15///
16/// Display notation: ".", "name1", "name1/name2", ...
17#[derive(Eq, PartialEq, Clone, Hash)]
18pub struct Moniker {
19    rep: FlyStr,
20}
21
22impl Moniker {
23    pub fn new(path: &[ChildName]) -> Self {
24        if path.is_empty() {
25            Self::root()
26        } else {
27            Self {
28                rep: path.into_iter().map(|s| s.as_ref()).collect::<Box<[&str]>>().join("/").into(),
29            }
30        }
31    }
32
33    fn new_unchecked<S: AsRef<str> + ?Sized>(rep: &S) -> Self {
34        Self { rep: rep.as_ref().into() }
35    }
36
37    pub fn new_from_borrowed(path: &[&BorrowedChildName]) -> Self {
38        if path.is_empty() {
39            Self::root()
40        } else {
41            Self {
42                rep: path
43                    .into_iter()
44                    .map(|s| (*s).as_ref())
45                    .collect::<Box<[&str]>>()
46                    .join("/")
47                    .into(),
48            }
49        }
50    }
51
52    pub fn path(&self) -> Box<[&BorrowedChildName]> {
53        if self.is_root() {
54            Box::new([])
55        } else {
56            self.rep.split('/').map(|s| BorrowedChildName::new_unchecked(s)).collect()
57        }
58    }
59
60    pub fn parse<T: AsRef<str>>(path: &[T]) -> Result<Self, MonikerError> {
61        if path.is_empty() {
62            return Ok(Self::root());
63        }
64        let path = path
65            .into_iter()
66            .map(|n| {
67                let _ = BorrowedChildName::parse(n.as_ref())?;
68                Ok(n.as_ref())
69            })
70            .collect::<Result<Box<[&str]>, MonikerError>>()?;
71        Ok(Self::new_unchecked(&path.join("/")))
72    }
73
74    pub fn parse_str(input: &str) -> Result<Self, MonikerError> {
75        if input.is_empty() {
76            return Err(MonikerError::invalid_moniker(input));
77        }
78        if input == "/" || input == "." || input == "./" {
79            return Ok(Self::root());
80        }
81
82        // Optionally strip a prefix of "/" or "./".
83        let stripped = match input.strip_prefix("/") {
84            Some(s) => s,
85            None => match input.strip_prefix("./") {
86                Some(s) => s,
87                None => input,
88            },
89        };
90        stripped
91            .split('/')
92            .into_iter()
93            .map(|s| {
94                let _ = BorrowedChildName::parse(s)?;
95                Ok::<(), MonikerError>(())
96            })
97            .collect::<Result<(), _>>()?;
98        Ok(Self::new_unchecked(stripped))
99    }
100
101    /// Concatenates other onto the end of this moniker.
102    pub fn concat(&self, other: &Moniker) -> Self {
103        let rep = if self.is_root() {
104            other.rep.clone()
105        } else if !other.is_root() {
106            format!("{}/{}", self.rep, other.rep).into()
107        } else {
108            self.rep.clone()
109        };
110        Self::new_unchecked(&rep)
111    }
112
113    /// Indicates whether this moniker is prefixed by prefix.
114    pub fn has_prefix(&self, prefix: &Moniker) -> bool {
115        if prefix.is_root() {
116            return true;
117        } else if self.path().len() < prefix.path().len() {
118            return false;
119        }
120
121        let my_segments =
122            self.rep.split('/').map(|s| BorrowedChildName::new_unchecked(s)).collect::<Box<_>>();
123        let prefix_segments =
124            prefix.rep.split('/').map(|s| BorrowedChildName::new_unchecked(s)).collect::<Box<_>>();
125        my_segments[..prefix_segments.len()] == *prefix_segments
126    }
127
128    pub fn root() -> Self {
129        Self { rep: ".".into() }
130    }
131
132    /// Returns the last child of this moniker if this is not the root moniker.
133    pub fn leaf(&self) -> Option<&BorrowedChildName> {
134        if self.is_root() {
135            None
136        } else {
137            let back = match self.rep.rfind('/') {
138                Some(i) => &self.rep[i + 1..],
139                None => &self.rep,
140            };
141            Some(BorrowedChildName::new_unchecked(back))
142        }
143    }
144
145    pub fn is_root(&self) -> bool {
146        self.rep == "."
147    }
148
149    /// Creates a new moniker with the last child removed. Returns `None` if this is the root
150    /// moniker.
151    pub fn parent(&self) -> Option<Self> {
152        if self.is_root() {
153            None
154        } else {
155            match self.rep.rfind('/') {
156                Some(i) => Some(Self::new_unchecked(&self.rep[0..i])),
157                None => Some(Self::root()),
158            }
159        }
160    }
161
162    /// Creates a new Moniker with `child` added to the end of this moniker.
163    pub fn child(&self, child: ChildName) -> Self {
164        if self.is_root() {
165            Self::new_unchecked(&child)
166        } else {
167            Self::new_unchecked(&format!("{self}/{child}"))
168        }
169    }
170
171    /// Splits off the last child of this moniker returning the parent as a moniker and the child.
172    /// Returns `None` if this is the root moniker.
173    pub fn split_leaf(&self) -> Option<(Self, &BorrowedChildName)> {
174        if self.is_root() {
175            None
176        } else {
177            let (rest, back) = match self.rep.rfind('/') {
178                Some(i) => {
179                    let path = Self::new_unchecked(&self.rep[0..i]);
180                    let back = BorrowedChildName::new_unchecked(&self.rep[i + 1..]);
181                    (path, back)
182                }
183                None => (Self::root(), BorrowedChildName::new_unchecked(&self.rep)),
184            };
185            Some((rest, back))
186        }
187    }
188
189    /// Strips the moniker parts in prefix from the beginning of this moniker.
190    pub fn strip_prefix(&self, prefix: &Moniker) -> Result<Self, MonikerError> {
191        if !self.has_prefix(prefix) {
192            return Err(MonikerError::MonikerDoesNotHavePrefix {
193                moniker: self.to_string(),
194                prefix: prefix.to_string(),
195            });
196        }
197
198        if prefix.is_root() {
199            Ok(self.clone())
200        } else if self == prefix {
201            Ok(Self::root())
202        } else {
203            assert!(!self.is_root(), "strip_prefix: caught by has_prefix above");
204            Ok(Self::new_unchecked(&self.rep[prefix.rep.len() + 1..]))
205        }
206    }
207}
208
209impl FidlIntoNative<Moniker> for String {
210    fn fidl_into_native(self) -> Moniker {
211        // This is used in routing::capability_source::CapabilitySource, and the FIDL version of
212        // this should only be generated in-process from already valid monikers.
213        self.parse().unwrap()
214    }
215}
216
217impl NativeIntoFidl<String> for Moniker {
218    fn native_into_fidl(self) -> String {
219        self.to_string()
220    }
221}
222
223impl Default for Moniker {
224    fn default() -> Self {
225        Self::root()
226    }
227}
228
229impl TryFrom<&[&str]> for Moniker {
230    type Error = MonikerError;
231
232    fn try_from(rep: &[&str]) -> Result<Self, MonikerError> {
233        Self::parse(rep)
234    }
235}
236
237impl<const N: usize> TryFrom<[&str; N]> for Moniker {
238    type Error = MonikerError;
239
240    fn try_from(rep: [&str; N]) -> Result<Self, MonikerError> {
241        Self::parse(&rep)
242    }
243}
244
245impl TryFrom<&str> for Moniker {
246    type Error = MonikerError;
247
248    fn try_from(input: &str) -> Result<Self, MonikerError> {
249        Self::parse_str(input)
250    }
251}
252
253impl std::str::FromStr for Moniker {
254    type Err = MonikerError;
255    fn from_str(s: &str) -> Result<Self, Self::Err> {
256        Self::parse_str(s)
257    }
258}
259
260impl cmp::Ord for Moniker {
261    fn cmp(&self, other: &Self) -> cmp::Ordering {
262        let self_path = self.path();
263        let other_path = other.path();
264        let min_size = cmp::min(self_path.len(), other_path.len());
265        for i in 0..min_size {
266            if self_path[i] < other_path[i] {
267                return cmp::Ordering::Less;
268            } else if self_path[i] > other_path[i] {
269                return cmp::Ordering::Greater;
270            }
271        }
272        if self_path.len() > other_path.len() {
273            return cmp::Ordering::Greater;
274        } else if self_path.len() < other_path.len() {
275            return cmp::Ordering::Less;
276        }
277
278        cmp::Ordering::Equal
279    }
280}
281
282impl PartialOrd for Moniker {
283    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
284        Some(self.cmp(other))
285    }
286}
287
288impl fmt::Display for Moniker {
289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290        write!(f, "{}", self.rep)
291    }
292}
293
294impl fmt::Debug for Moniker {
295    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296        write!(f, "{self}")
297    }
298}
299
300#[cfg(test)]
301mod tests {
302    use super::*;
303    use cm_types::BorrowedName;
304
305    #[test]
306    fn monikers() {
307        let root = Moniker::root();
308        assert_eq!(true, root.is_root());
309        assert_eq!(".", format!("{}", root));
310        assert_eq!(root, Moniker::new(&[]));
311        assert_eq!(root, Moniker::try_from([]).unwrap());
312
313        let m = Moniker::new(&[
314            ChildName::try_new("a", None).unwrap(),
315            ChildName::try_new("b", Some("coll")).unwrap(),
316        ]);
317        assert_eq!(false, m.is_root());
318        assert_eq!("a/coll:b", format!("{}", m));
319        assert_eq!(m, Moniker::try_from(["a", "coll:b"]).unwrap());
320        assert_eq!(
321            m.leaf().map(|m| m.collection()).flatten(),
322            Some(BorrowedName::new("coll").unwrap())
323        );
324        assert_eq!(m.leaf().map(|m| m.name().as_str()), Some("b"));
325        assert_eq!(m.leaf(), Some(BorrowedChildName::parse("coll:b").unwrap()));
326    }
327
328    #[test]
329    fn moniker_parent() {
330        let root = Moniker::root();
331        assert_eq!(true, root.is_root());
332        assert_eq!(None, root.parent());
333
334        let m = Moniker::new(&[
335            ChildName::try_new("a", None).unwrap(),
336            ChildName::try_new("b", None).unwrap(),
337        ]);
338        assert_eq!("a/b", format!("{}", m));
339        assert_eq!("a", format!("{}", m.parent().unwrap()));
340        assert_eq!(".", format!("{}", m.parent().unwrap().parent().unwrap()));
341        assert_eq!(None, m.parent().unwrap().parent().unwrap().parent());
342        assert_eq!(m.leaf(), Some(BorrowedChildName::parse("b").unwrap()));
343    }
344
345    #[test]
346    fn moniker_concat() {
347        let scope_root: Moniker = ["a:test1", "b:test2"].try_into().unwrap();
348
349        let relative: Moniker = ["c:test3", "d:test4"].try_into().unwrap();
350        let descendant = scope_root.concat(&relative);
351        assert_eq!("a:test1/b:test2/c:test3/d:test4", format!("{}", descendant));
352
353        let relative: Moniker = [].try_into().unwrap();
354        let descendant = scope_root.concat(&relative);
355        assert_eq!("a:test1/b:test2", format!("{}", descendant));
356    }
357
358    #[test]
359    fn moniker_parse_str() {
360        assert_eq!(Moniker::try_from("/foo").unwrap(), Moniker::try_from(["foo"]).unwrap());
361        assert_eq!(Moniker::try_from("./foo").unwrap(), Moniker::try_from(["foo"]).unwrap());
362        assert_eq!(Moniker::try_from("foo").unwrap(), Moniker::try_from(["foo"]).unwrap());
363        assert_eq!(Moniker::try_from("/").unwrap(), Moniker::try_from([]).unwrap());
364        assert_eq!(Moniker::try_from("./").unwrap(), Moniker::try_from([]).unwrap());
365
366        assert!(Moniker::try_from("//foo").is_err());
367        assert!(Moniker::try_from(".//foo").is_err());
368        assert!(Moniker::try_from("/./foo").is_err());
369        assert!(Moniker::try_from("../foo").is_err());
370        assert!(Moniker::try_from(".foo").is_err());
371    }
372
373    #[test]
374    fn moniker_has_prefix() {
375        assert!(Moniker::parse_str("a").unwrap().has_prefix(&Moniker::parse_str("a").unwrap()));
376        assert!(Moniker::parse_str("a/b").unwrap().has_prefix(&Moniker::parse_str("a").unwrap()));
377        assert!(Moniker::parse_str("a/b:test")
378            .unwrap()
379            .has_prefix(&Moniker::parse_str("a").unwrap()));
380        assert!(Moniker::parse_str("a/b/c/d")
381            .unwrap()
382            .has_prefix(&Moniker::parse_str("a/b/c").unwrap()));
383        assert!(!Moniker::parse_str("a/b")
384            .unwrap()
385            .has_prefix(&Moniker::parse_str("a/b/c").unwrap()));
386        assert!(!Moniker::parse_str("a/c")
387            .unwrap()
388            .has_prefix(&Moniker::parse_str("a/b/c").unwrap()));
389        assert!(!Moniker::root().has_prefix(&Moniker::parse_str("a").unwrap()));
390        assert!(!Moniker::parse_str("a/b:test")
391            .unwrap()
392            .has_prefix(&Moniker::parse_str("a/b").unwrap()));
393    }
394
395    #[test]
396    fn moniker_child() {
397        assert_eq!(
398            Moniker::root().child(ChildName::try_from("a").unwrap()),
399            Moniker::parse_str("a").unwrap()
400        );
401        assert_eq!(
402            Moniker::parse_str("a").unwrap().child(ChildName::try_from("b").unwrap()),
403            Moniker::parse_str("a/b").unwrap()
404        );
405        assert_eq!(
406            Moniker::parse_str("a:test").unwrap().child(ChildName::try_from("b").unwrap()),
407            Moniker::parse_str("a:test/b").unwrap()
408        );
409        assert_eq!(
410            Moniker::parse_str("a").unwrap().child(ChildName::try_from("b:test").unwrap()),
411            Moniker::parse_str("a/b:test").unwrap()
412        );
413    }
414
415    #[test]
416    fn moniker_split_leaf() {
417        assert_eq!(Moniker::root().split_leaf(), None);
418        assert_eq!(
419            Moniker::parse_str("a/b:test").unwrap().split_leaf(),
420            Some((Moniker::parse_str("a").unwrap(), BorrowedChildName::parse("b:test").unwrap()))
421        );
422    }
423
424    #[test]
425    fn moniker_strip_prefix() {
426        assert_eq!(
427            Moniker::parse_str("a").unwrap().strip_prefix(&Moniker::parse_str("a").unwrap()),
428            Ok(Moniker::root())
429        );
430        assert_eq!(
431            Moniker::parse_str("a/b").unwrap().strip_prefix(&Moniker::parse_str("a").unwrap()),
432            Ok(Moniker::parse_str("b").unwrap())
433        );
434        assert!(Moniker::parse_str("a/b")
435            .unwrap()
436            .strip_prefix(&Moniker::parse_str("b").unwrap())
437            .is_err());
438        assert!(Moniker::root().strip_prefix(&Moniker::parse_str("b").unwrap()).is_err());
439    }
440}