parse/
upper_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 UpperName {
23    pub (crate) inner: Cow<'static, str>,
24}
25
26impl UpperName {
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        UpperName {
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_uppercase() && c != '_' {
48                    return Err(NameError::StartUppercase {
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(UpperName {
63            inner: Cow::Owned(value.to_owned()),
64        })
65    }
66
67    pub fn unchecked_from(str: &'static str) -> Self {
68        UpperName {
69            inner: Cow::Borrowed(str),
70        }
71    }
72}
73
74impl Borrow<str> for UpperName {
75    fn borrow(&self) -> &str {
76        self.as_str()
77    }
78}
79
80impl Debug for UpperName {
81    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
82        write!(f, "UpperName({})", self.as_str())
83    }
84}
85
86impl Display for UpperName {
87    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
88        write!(f, "{}", self.as_str())
89    }
90}
91
92impl From<UpperName> for String {
93    fn from(name: UpperName) -> String {
94        name.inner.into_owned()
95    }
96}
97
98impl<'input> TryFrom<Attribute<'input, 'input>> for UpperName {
99    type Error = NameError;
100
101    fn try_from(attribute: Attribute<'input, 'input>) -> Result<Self, Self::Error> {
102        if attribute.value().is_empty() {
103            return Err(NameError::Empty {
104                name: attribute.range().into(),
105            });
106        }
107
108        UpperName::try_from(attribute.value(), attribute.range_value())
109    }
110}
111
112impl<'input> TryFrom<Node<'input, 'input>> for UpperName {
113    type Error = NameError;
114
115    fn try_from(node: Node<'input, 'input>) -> Result<Self, Self::Error> {
116        if let Some(value) = node.text() {
117            if value.is_empty() {
118                return Err(NameError::Empty {
119                    name: node.range().into(),
120                });
121            }
122
123            UpperName::try_from(value, node.range())
124        } else {
125            Err(NameError::Empty {
126                name: node.range().into(),
127            })
128        }
129    }
130}
131
132impl PartialEq<str> for UpperName {
133    fn eq(&self, other: &str) -> bool {
134        PartialEq::eq(self.as_str(), other)
135    }
136}
137
138impl PartialOrd<str> for UpperName {
139    fn partial_cmp(&self, other: &str) -> Option<Ordering> {
140        PartialOrd::partial_cmp(self.as_str(), other)
141    }
142}