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}