1use 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#[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 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 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 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 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 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 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 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 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 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}