Skip to main content

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