1use 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#[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 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#[derive(Eq, PartialEq)]
209#[repr(transparent)]
210pub struct BorrowedChildName {
211 rep: str,
212}
213
214impl BorrowedChildName {
215 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 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}