parse/
lower_name.rs

1use roxmltree::{
2    Attribute,
3    Node,
4};
5use std::borrow::{
6    Cow,
7    Borrow,
8};
9use std::fmt::{
10    Debug,
11    Display,
12    Formatter,
13};
14use std::cmp::Ordering;
15use std::ops::Range;
16
17use super::NameError;
18
19#[derive(Clone)]
20#[derive(Eq, PartialEq)]
21#[derive(Ord, PartialOrd)]
22pub struct LowerName {
23    pub (crate) inner: Cow<'static, str>,
24}
25
26impl LowerName {
27    pub fn as_str(&self) -> &str {
28        self.inner.as_ref()
29    }
30
31    pub fn append(&self, rhs: &str) -> Self {
32        let lhs = self.as_str();
33        let mut s = String::with_capacity(lhs.len() + rhs.len());
34        s.push_str(lhs);
35        s.push_str(rhs);
36        LowerName {
37            inner: Cow::Owned(s),
38        }
39    }
40
41    fn try_from(value: &str, mut range: Range<usize>) -> Result<Self, NameError> {
42        let mut first = true;
43        for c in value.chars() {
44            if first {
45                first = false;
46
47                if !c.is_ascii_lowercase() && c != '_' {
48                    return Err(NameError::StartLowercase {
49                        name: range.into(),
50                    });
51                }
52            } else {
53                if !c.is_ascii_alphanumeric() {
54                    return Err(NameError::Alphanumeric {
55                        name: range.into(),
56                    });
57                }
58            }
59            range.start += 1;
60        }
61
62        Ok(LowerName {
63            inner: Cow::Owned(value.to_owned()),
64        })
65    }
66
67    pub fn unchecked_from(str: &'static str) -> Self {
68        LowerName {
69            inner: Cow::Borrowed(str),
70        }
71    }
72}
73
74impl Borrow<str> for LowerName {
75    fn borrow(&self) -> &str {
76        self.as_str()
77    }
78}
79
80impl Debug for LowerName {
81    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
82        write!(f, "LowerName({})", self.as_str())
83    }
84}
85
86impl Display for LowerName {
87    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
88        write!(f, "{}", self.as_str())
89    }
90}
91
92impl<'input> TryFrom<Attribute<'input, 'input>> for LowerName {
93    type Error = NameError;
94
95    fn try_from(attribute: Attribute<'input, 'input>) -> Result<Self, Self::Error> {
96        if attribute.value().is_empty() {
97            return Err(NameError::Empty {
98                name: attribute.range().into(),
99            });
100        }
101
102        LowerName::try_from(attribute.value(), attribute.range_value())
103    }
104}
105
106impl<'input> TryFrom<Node<'input, 'input>> for LowerName {
107    type Error = NameError;
108
109    fn try_from(node: Node<'input, 'input>) -> Result<Self, Self::Error> {
110        if let Some(value) = node.text() {
111            if value.is_empty() {
112                return Err(NameError::Empty {
113                    name: node.range().into(),
114                });
115            }
116
117            LowerName::try_from(value, node.range())
118        } else {
119            Err(NameError::Empty {
120                name: node.range().into(),
121            })
122        }
123    }
124}
125
126impl PartialEq<str> for LowerName {
127    fn eq(&self, other: &str) -> bool {
128        PartialEq::eq(self.as_str(), other)
129    }
130}
131
132impl PartialOrd<str> for LowerName {
133    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
134        PartialOrd::partial_cmp(self.as_str(), other)
135    }
136}