1use std::cmp::Ordering;
2use std::fmt;
3
4#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Hash)]
14pub struct Variable {
15 name: String,
16}
17
18impl Variable {
19 pub fn new(name: impl Into<String>) -> Result<Self, VariableNameParseError> {
23 let name = name.into();
24 validate_variable_identifier(&name)?;
25 Ok(Self::new_unchecked(name))
26 }
27
28 #[inline]
35 pub fn new_unchecked(name: impl Into<String>) -> Self {
36 Self { name: name.into() }
37 }
38
39 #[inline]
40 pub fn as_str(&self) -> &str {
41 &self.name
42 }
43
44 #[inline]
45 pub fn into_string(self) -> String {
46 self.name
47 }
48
49 #[inline]
50 pub fn as_ref(&self) -> VariableRef<'_> {
51 VariableRef { name: &self.name }
52 }
53}
54
55impl fmt::Display for Variable {
56 #[inline]
57 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58 self.as_ref().fmt(f)
59 }
60}
61
62#[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy, Hash)]
72pub struct VariableRef<'a> {
73 name: &'a str,
74}
75
76impl<'a> VariableRef<'a> {
77 pub fn new(name: &'a str) -> Result<Self, VariableNameParseError> {
81 validate_variable_identifier(name)?;
82 Ok(Self::new_unchecked(name))
83 }
84
85 #[inline]
92 pub const fn new_unchecked(name: &'a str) -> Self {
93 Self { name }
94 }
95
96 #[inline]
97 pub const fn as_str(self) -> &'a str {
98 self.name
99 }
100
101 #[inline]
102 pub fn into_string(self) -> String {
103 self.name.to_owned()
104 }
105
106 #[inline]
107 pub fn into_owned(self) -> Variable {
108 Variable {
109 name: self.name.to_owned(),
110 }
111 }
112}
113
114impl fmt::Display for VariableRef<'_> {
115 #[inline]
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 write!(f, "?{}", self.name)
118 }
119}
120
121impl<'a> From<&'a Variable> for VariableRef<'a> {
122 #[inline]
123 fn from(variable: &'a Variable) -> Self {
124 variable.as_ref()
125 }
126}
127
128impl<'a> From<VariableRef<'a>> for Variable {
129 #[inline]
130 fn from(variable: VariableRef<'a>) -> Self {
131 variable.into_owned()
132 }
133}
134
135impl PartialEq<Variable> for VariableRef<'_> {
136 #[inline]
137 fn eq(&self, other: &Variable) -> bool {
138 *self == other.as_ref()
139 }
140}
141
142impl PartialEq<VariableRef<'_>> for Variable {
143 #[inline]
144 fn eq(&self, other: &VariableRef<'_>) -> bool {
145 self.as_ref() == *other
146 }
147}
148
149impl PartialOrd<Variable> for VariableRef<'_> {
150 #[inline]
151 fn partial_cmp(&self, other: &Variable) -> Option<Ordering> {
152 self.partial_cmp(&other.as_ref())
153 }
154}
155
156impl PartialOrd<VariableRef<'_>> for Variable {
157 #[inline]
158 fn partial_cmp(&self, other: &VariableRef<'_>) -> Option<Ordering> {
159 self.as_ref().partial_cmp(other)
160 }
161}
162
163fn validate_variable_identifier(id: &str) -> Result<(), VariableNameParseError> {
164 let mut chars = id.chars();
165 let front = chars.next().ok_or(VariableNameParseError)?;
166 match front {
167 '0'..='9'
168 | '_'
169 | ':'
170 | 'A'..='Z'
171 | 'a'..='z'
172 | '\u{00C0}'..='\u{00D6}'
173 | '\u{00D8}'..='\u{00F6}'
174 | '\u{00F8}'..='\u{02FF}'
175 | '\u{0370}'..='\u{037D}'
176 | '\u{037F}'..='\u{1FFF}'
177 | '\u{200C}'..='\u{200D}'
178 | '\u{2070}'..='\u{218F}'
179 | '\u{2C00}'..='\u{2FEF}'
180 | '\u{3001}'..='\u{D7FF}'
181 | '\u{F900}'..='\u{FDCF}'
182 | '\u{FDF0}'..='\u{FFFD}'
183 | '\u{10000}'..='\u{EFFFF}' => (),
184 _ => return Err(VariableNameParseError),
185 }
186 for c in chars {
187 match c {
188 '0'..='9'
189 | '\u{00B7}'
190 | '\u{0300}'..='\u{036F}'
191 | '\u{203F}'..='\u{2040}'
192 | '_'
193 | 'A'..='Z'
194 | 'a'..='z'
195 | '\u{00C0}'..='\u{00D6}'
196 | '\u{00D8}'..='\u{00F6}'
197 | '\u{00F8}'..='\u{02FF}'
198 | '\u{0370}'..='\u{037D}'
199 | '\u{037F}'..='\u{1FFF}'
200 | '\u{200C}'..='\u{200D}'
201 | '\u{2070}'..='\u{218F}'
202 | '\u{2C00}'..='\u{2FEF}'
203 | '\u{3001}'..='\u{D7FF}'
204 | '\u{F900}'..='\u{FDCF}'
205 | '\u{FDF0}'..='\u{FFFD}'
206 | '\u{10000}'..='\u{EFFFF}' => (),
207 _ => return Err(VariableNameParseError),
208 }
209 }
210 Ok(())
211}
212
213#[derive(Debug, thiserror::Error)]
215#[error("The variable name is invalid")]
216pub struct VariableNameParseError;