1use std::fmt::{self, Debug, Display};
5use std::hash::{Hash, Hasher};
6use std::convert::Infallible;
7use std::borrow::Borrow;
8use std::num::NonZero;
9use std::str::FromStr;
10use std::ops::Deref;
11use std::sync::Arc;
12
13
14#[repr(transparent)]
22pub struct Name(str);
23
24impl Name {
25
26 #[inline]
29 pub fn new(name: &str) -> &Self {
30 unsafe { &*(name as *const str as *const Self) }
33 }
34
35 #[inline]
37 pub fn as_str(&self) -> &str {
38 &self.0
39 }
40
41 #[inline]
43 pub fn to_arc(&self) -> ArcName {
44 ArcName(Arc::from(&self.0))
45 }
46
47}
48
49impl PartialEq for Name {
50 #[inline]
51 fn eq(&self, other: &Self) -> bool {
52 self.0.eq_ignore_ascii_case(&other.0)
53 }
54}
55
56impl Eq for Name {}
57
58impl Hash for Name {
59 fn hash<H: Hasher>(&self, state: &mut H) {
60 for byte in self.0.bytes() {
64 state.write_u8(byte.to_ascii_lowercase());
65 }
66 state.write_u8(0xff);
67 }
68}
69
70impl Debug for Name {
71 #[inline]
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 Debug::fmt(&self.0, f)
74 }
75}
76
77impl Display for Name {
78 #[inline]
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 Display::fmt(&self.0, f)
81 }
82}
83
84pub struct ArcName(Arc<str>);
86
87impl From<Arc<str>> for ArcName {
88 fn from(value: Arc<str>) -> Self {
89 Self(value)
90 }
91}
92
93impl From<&str> for ArcName {
94 #[inline]
95 fn from(value: &str) -> Self {
96 Self(Arc::from(value))
97 }
98}
99
100impl FromStr for ArcName {
101
102 type Err = Infallible;
103
104 #[inline]
105 fn from_str(s: &str) -> Result<Self, Self::Err> {
106 Ok(Self::from(s))
107 }
108
109}
110
111impl Deref for ArcName {
112 type Target = Name;
113 #[inline]
114 fn deref(&self) -> &Self::Target {
115 Name::new(&self.0)
116 }
117}
118
119impl Borrow<Name> for ArcName {
120 #[inline]
121 fn borrow(&self) -> &Name {
122 &**self
123 }
124}
125
126impl PartialEq for ArcName {
127 fn eq(&self, other: &Self) -> bool {
128 **self == **other
129 }
130}
131
132impl Eq for ArcName {}
133
134impl Hash for ArcName {
135 fn hash<H: Hasher>(&self, state: &mut H) {
136 (**self).hash(state);
137 }
138}
139
140impl Debug for ArcName {
141 #[inline]
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 Debug::fmt(&**self, f)
144 }
145}
146
147impl Display for ArcName {
148 #[inline]
149 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150 Display::fmt(&**self, f)
151 }
152}
153
154
155#[derive(thiserror::Error, Debug)]
156pub enum ParseTrlError {
157 #[error("Invalid protocol, expecting tango://")]
158 InvalidProtocol,
159 #[error("Invalid host, wrong characters or too long (> 255)")]
160 InvalidHost,
161 #[error("Invalid port, expecting non-null 16-bit integer")]
162 InvalidPort,
163 #[error("Invalid hash, expecting #dbase=yes/no")]
164 InvalidHash,
165 #[error("Invalid or missing path, device-name[/attribute] with device-name being an alias or domain/family/member")]
166 InvalidPath,
167 #[error("Invalid device name, wrong characters or too long (> 85 per domain/family/member)")]
168 InvalidDeviceName,
169 #[error("Invalid attribute name, wrong characters or too long (> 255)")]
170 InvalidAttrName,
171 #[error("Invalid device or attribute alias, wrong characters or too long (> 255)")]
172 InvalidAlias,
173 #[error("Invalid property name, wrong characters or too long (> 255)")]
174 InvalidPropertyName,
175}
176
177#[derive(Clone, Copy)]
188pub struct Trl<'a> {
189 raw: &'a str,
191 meta: TrlMeta,
193}
194
195#[derive(Clone)]
200pub struct ArcTrl {
201 raw: Arc<str>,
203 meta: TrlMeta,
205}
206
207#[derive(Debug, Clone, Copy)]
209struct TrlMeta {
210 host_len_and_port: Option<(NonZero<u8>, NonZero<u16>)>,
212 object_index: u16,
214 object: TrlObjectMeta,
216 property_len: Option<NonZero<u8>>,
218 protocol: bool,
220 db: bool,
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
230enum TrlObjectMeta {
231 Alias(NonZero<u8>),
232 Dev(NonZero<u8>),
233 Attr(NonZero<u8>, NonZero<u8>),
234}
235
236impl TrlMeta {
237
238 fn parse(raw: &str) -> Result<Self, ParseTrlError> {
239
240 let mut rem = raw;
241 let mut object_offset = 0u16;
242
243 let mut protocol = false;
245 if let Some((proto, new_rem)) = rem.split_once("://") {
246 if proto != "tango" {
247 return Err(ParseTrlError::InvalidProtocol);
248 }
249 object_offset += "tango://".len() as u16;
250 protocol = true;
251 rem = new_rem;
252 }
253
254 let mut db = true;
256 if let Some((new_rem, hash)) = rem.rsplit_once('#') {
257 db = match hash {
258 "dbase=yes" => true,
259 "dbase=no" => false,
260 _ => return Err(ParseTrlError::InvalidHash),
261 };
262 rem = new_rem;
263 }
264
265 let mut property_len = None;
268 if let Some((new_rem, property_raw)) = rem.rsplit_once("->") {
269
270 property_len = Some(u8::try_from(property_raw.len()).ok()
271 .and_then(NonZero::new)
272 .ok_or(ParseTrlError::InvalidPropertyName)?);
273
274 if !property_raw.chars().enumerate().all(is_tango_name_char) {
275 return Err(ParseTrlError::InvalidPropertyName);
276 }
277
278 rem = new_rem;
279
280 }
281
282 let mut parts = [""; 5];
284 let mut parts_count = 0;
285 for part in rem.split('/') {
286 if parts_count >= parts.len() {
288 return Err(ParseTrlError::InvalidPath);
289 }
290 parts[parts_count] = part;
291 parts_count += 1;
292 }
293
294
295 let mut parts = &mut parts[..parts_count];
297 debug_assert!(!parts.is_empty(), "A split we should have at least one element");
298
299 let mut host_len_and_port = None;
301 if let Some((host_raw, port_raw)) = parts[0].split_once(':') {
302
303 parts = &mut parts[1..];
305 if parts.is_empty() {
306 return Err(ParseTrlError::InvalidPath)
307 }
308
309 let host_len = u8::try_from(host_raw.len()).ok()
310 .and_then(NonZero::new)
311 .ok_or(ParseTrlError::InvalidHost)?;
312
313 if !host_raw.chars().enumerate().all(is_hostname_char) {
314 return Err(ParseTrlError::InvalidHost);
315 }
316
317 let port = port_raw.parse::<NonZero<u16>>()
318 .map_err(|_| ParseTrlError::InvalidPort)?;
319
320 host_len_and_port = Some((host_len, port));
321
322 object_offset += host_raw.len() as u16 + 1 + port_raw.len() as u16 + 1;
324
325 }
326
327 let object;
329 if parts.len() == 1 {
330
331 object = TrlObjectMeta::Alias(u8::try_from(parts[0].len()).ok()
332 .and_then(NonZero::new)
333 .ok_or(ParseTrlError::InvalidAlias)?);
334
335 if !parts[0].chars().enumerate().all(is_tango_name_char) {
336 return Err(ParseTrlError::InvalidAlias)
337 }
338
339 } else if parts.len() == 3 || parts.len() == 4 {
340
341 let mut dev_len_raw = 0; for dev_part in &parts[..3] {
343
344 if dev_part.len() == 0 || dev_part.len() > 85 {
345 return Err(ParseTrlError::InvalidDeviceName);
346 }
347
348 if !dev_part.chars().enumerate().all(is_tango_name_char) {
349 return Err(ParseTrlError::InvalidDeviceName);
350 }
351
352 dev_len_raw += dev_part.len();
353
354 }
355
356 let dev_len = NonZero::new(dev_len_raw as u8).unwrap();
358
359 if let Some(&attr_raw) = parts.get(3) {
360
361 object = TrlObjectMeta::Attr(dev_len, u8::try_from(attr_raw.len()).ok()
362 .and_then(NonZero::new)
363 .ok_or(ParseTrlError::InvalidAttrName)?);
364
365 if !attr_raw.chars().enumerate().all(is_tango_name_char) {
366 return Err(ParseTrlError::InvalidAttrName);
367 }
368
369 } else {
370 object = TrlObjectMeta::Dev(dev_len);
371 }
372
373 } else {
374 return Err(ParseTrlError::InvalidPath);
375 }
376
377 Ok(Self {
378 host_len_and_port,
379 object_index: object_offset,
380 object,
381 property_len,
382 protocol,
383 db,
384 })
385
386 }
387
388}
389
390impl<'a> Trl<'a> {
391
392 pub fn parse(raw: &'a str) -> Result<Self, ParseTrlError> {
394 Ok(Self { raw, meta: TrlMeta::parse(raw)? })
395 }
396
397 #[inline]
399 pub fn as_str(self) -> &'a str {
400 self.raw
401 }
402
403 #[inline]
405 pub fn to_arc(self) -> ArcTrl {
406 ArcTrl { raw: Arc::from(self.raw), meta: self.meta }
407 }
408
409 #[inline]
410 fn host_and_port_index(self) -> usize {
411 if self.meta.protocol { "tango://".len() } else { 0 }
412 }
413
414 pub fn host_and_port(self) -> Option<(&'a str, u16)> {
415 let index = self.host_and_port_index();
416 self.meta.host_len_and_port.map(|(host_len, port)| {
417 (&self.raw[index..][..host_len.get() as usize], port.get())
418 })
419 }
420
421 #[inline]
422 fn object_index(self) -> usize {
423 self.meta.object_index as usize
424 }
425
426 pub fn object(self) -> TrlObject<'a> {
427 let index = self.object_index();
428 match self.meta.object {
429 TrlObjectMeta::Alias(alias_len) => {
430 TrlObject::Alias(&self.raw[index..][..alias_len.get() as usize])
431 }
432 TrlObjectMeta::Dev(dev_len) => {
433 TrlObject::Dev(&self.raw[index..][..dev_len.get() as usize + 2])
435 }
436 TrlObjectMeta::Attr(dev_len, attr_len) => {
437 let (dev_name, raw) = self.raw[index..].split_at(dev_len.get() as usize + 2);
439 let attr_name = &raw[1..][..attr_len.get() as usize];
441 TrlObject::Attr(dev_name, attr_name)
442 }
443 }
444 }
445
446 #[inline]
447 fn property_index(self) -> usize {
448 self.object_index() + match self.meta.object {
449 TrlObjectMeta::Alias(alias_len) =>
450 alias_len.get() as usize,
451 TrlObjectMeta::Dev(dev_len) =>
452 dev_len.get() as usize + 2,
453 TrlObjectMeta::Attr(dev_len, attr_len) =>
454 dev_len.get() as usize + attr_len.get() as usize + 3,
455 } + 2 }
457
458 pub fn property(self) -> Option<&'a str> {
459 let index = self.property_index();
460 self.meta.property_len.map(|len| {
461 &self.raw[index..][..len.get() as usize]
462 })
463 }
464
465 #[inline]
466 pub fn db(self) -> bool {
467 self.meta.db
468 }
469
470}
471
472impl ArcTrl {
473
474 #[inline]
476 pub fn as_trl(&self) -> Trl<'_> {
477 Trl { raw: &self.raw, meta: self.meta }
478 }
479
480 #[inline]
482 pub fn as_str(&self) -> &str {
483 &self.raw
484 }
485
486}
487
488impl FromStr for ArcTrl {
489
490 type Err = ParseTrlError;
491
492 fn from_str(raw: &str) -> Result<Self, Self::Err> {
493 Ok(Self { raw: Arc::from(raw), meta: TrlMeta::parse(raw)? })
494 }
495
496}
497
498impl From<Trl<'_>> for ArcTrl {
499 #[inline]
500 fn from(trl: Trl<'_>) -> Self {
501 trl.to_arc()
502 }
503}
504
505impl<'a> From<&'a ArcTrl> for Trl<'a> {
506 #[inline]
507 fn from(trl: &'a ArcTrl) -> Self {
508 trl.as_trl()
509 }
510}
511
512impl fmt::Debug for Trl<'_> {
513 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
514 f.debug_tuple("Trl").field(&self.raw).finish()
515 }
516}
517
518impl fmt::Display for Trl<'_> {
519 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
520 f.write_str(self.raw)
521 }
522}
523
524impl fmt::Debug for ArcTrl {
525 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
526 f.debug_tuple("ArcTrl").field(&&*self.raw).finish()
527 }
528}
529
530impl fmt::Display for ArcTrl {
531 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
532 f.write_str(&self.raw)
533 }
534}
535
536pub enum TrlObject<'a> {
538 Alias(&'a str),
540 Dev(&'a str),
542 Attr(&'a str, &'a str),
544}
545
546fn is_hostname_char((i, c): (usize, char)) -> bool {
548 c.is_ascii_alphanumeric() || (i != 0 && (c == '-' || c == '.'))
549}
550
551fn is_tango_name_char((i, c): (usize, char)) -> bool {
557 c.is_ascii_alphanumeric() || (i != 0 && (c == '_' || c == '-'))
558}
559
560pub trait IntoTrl<'a> {
565 fn into_trl(self) -> Result<Trl<'a>, ParseTrlError>;
566}
567
568impl<'a> IntoTrl<'a> for Trl<'a> {
569 fn into_trl(self) -> Result<Trl<'a>, ParseTrlError> {
570 Ok(self)
571 }
572}
573
574impl<'a> IntoTrl<'a> for &'a ArcTrl {
575 fn into_trl(self) -> Result<Trl<'a>, ParseTrlError> {
576 Ok(self.as_trl())
577 }
578}
579
580impl<'a> IntoTrl<'a> for &'a str {
581 fn into_trl(self) -> Result<Trl<'a>, ParseTrlError> {
582 Trl::parse(self)
583 }
584}
585
586
587#[cfg(test)]
588mod tests {
589
590 use super::*;
591 use std::num::NonZero;
592 use std::collections::HashSet;
593
594 #[test]
595 fn name_ignores_case() {
596
597 assert_eq!(Name::new("Sys/TG_Test/1"), Name::new("sys/tg_test/1"));
598 assert_ne!(Name::new("sys/tg_test/1"), Name::new("sys/tg_test/2"));
599 assert_ne!(Name::new("attr"), Name::new("attr_2"));
600
601 let names = HashSet::from([ArcName::from("Double_Scalar")]);
602 assert!(names.contains(Name::new("double_scalar")));
603 assert!(!names.contains(Name::new("double_scalar_w")));
604
605 }
606
607 #[test]
608 fn name_keeps_case() {
609
610 let name = Name::new("Double_Scalar");
611 assert_eq!(name.as_str(), "Double_Scalar");
612 assert_eq!(name.to_string(), "Double_Scalar");
613
614 let arc_name = name.to_arc();
615 assert_eq!(arc_name.as_str(), "Double_Scalar");
616 assert_eq!(arc_name.to_string(), "Double_Scalar");
617 assert_eq!("double_scalar".parse::<ArcName>().unwrap(), arc_name);
618
619 }
620
621 #[test]
622 fn parse_and_get() {
623
624 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
625 enum TrlObjectKind {
626 Alias,
627 Dev,
628 Attr,
629 }
630
631 for protocol in [false, true] {
632 for host_and_port in [false, true] {
633 for object in [TrlObjectKind::Alias, TrlObjectKind::Dev, TrlObjectKind::Attr] {
634 for property in [false, true] {
635 for db in [None, Some(false), Some(true)] {
636
637 let mut buf = String::new();
638
639 if protocol {
640 buf.push_str("tango://");
641 }
642
643 if host_and_port {
644 buf.push_str("example.com:10000/");
645 }
646
647 let object_index = buf.len() as u16;
648 match object {
649 TrlObjectKind::Alias => buf.push_str("alias"),
650 TrlObjectKind::Dev => buf.push_str("domain/family/member"),
651 TrlObjectKind::Attr => buf.push_str("domain/family/member/attribute"),
652 }
653
654 if property {
655 buf.push_str("->property");
656 }
657
658 match db {
659 Some(false) => buf.push_str("#dbase=no"),
660 Some(true) => buf.push_str("#dbase=yes"),
661 None => (),
662 }
663
664 let trl = Trl::parse(&buf).unwrap();
665
666 assert_eq!(trl.meta.protocol, protocol);
667 assert_eq!(trl.meta.host_len_and_port, host_and_port.then_some((NonZero::new(11u8).unwrap(), NonZero::new(10000u16).unwrap())));
668 assert_eq!(trl.meta.object_index, object_index);
669 assert_eq!(trl.meta.object, match object {
670 TrlObjectKind::Alias => TrlObjectMeta::Alias(NonZero::new(5u8).unwrap()),
671 TrlObjectKind::Dev => TrlObjectMeta::Dev(NonZero::new(18u8).unwrap()),
672 TrlObjectKind::Attr => TrlObjectMeta::Attr(NonZero::new(18u8).unwrap(), NonZero::new(9u8).unwrap()),
673 });
674 assert_eq!(trl.meta.property_len, property.then_some(NonZero::new(8u8).unwrap()));
675 assert_eq!(trl.meta.db, db.unwrap_or(true));
676
677 }
678 }
679 }
680 }
681 }
682
683 }
684
685}