1use std::rc::Rc;
12
13use bstr::BString;
14use thiserror::Error;
15
16use crate::types;
17use crate::types::{Array, TypeValue};
18
19pub struct Variable(TypeValue);
24
25#[derive(Error, Debug, Eq, PartialEq)]
27pub enum VariableError {
28 #[error("variable `{0}` not defined")]
32 Undefined(String),
33
34 #[error("variable `{0}` already exists")]
36 AlreadyExists(String),
37
38 #[error("invalid identifier `{0}`")]
41 InvalidIdentifier(String),
42
43 #[error("null values are not accepted")]
46 UnexpectedNull,
47
48 #[error(
51 "arrays can't be empty and all items must be non-null and the same type"
52 )]
53 InvalidArray,
54
55 #[error("integer value is out of range")]
57 IntegerOutOfRange,
58
59 #[error(
63 "invalid type for `{variable}`, expecting `{expected_type}`, got `{actual_type}"
64 )]
65 InvalidType {
66 variable: String,
68 expected_type: String,
70 actual_type: String,
72 },
73}
74
75impl TryFrom<bool> for Variable {
76 type Error = VariableError;
77 fn try_from(value: bool) -> Result<Self, Self::Error> {
78 Ok(Variable(TypeValue::var_bool_from(value)))
79 }
80}
81
82impl TryFrom<i64> for Variable {
83 type Error = VariableError;
84 fn try_from(value: i64) -> Result<Self, Self::Error> {
85 Ok(Variable(TypeValue::var_integer_from(value)))
86 }
87}
88
89impl TryFrom<i32> for Variable {
90 type Error = VariableError;
91 fn try_from(value: i32) -> Result<Self, Self::Error> {
92 Ok(Variable(TypeValue::var_integer_from(value)))
93 }
94}
95
96impl TryFrom<i16> for Variable {
97 type Error = VariableError;
98 fn try_from(value: i16) -> Result<Self, Self::Error> {
99 Ok(Variable(TypeValue::var_integer_from(value)))
100 }
101}
102
103impl TryFrom<i8> for Variable {
104 type Error = VariableError;
105 fn try_from(value: i8) -> Result<Self, Self::Error> {
106 Ok(Variable(TypeValue::var_integer_from(value)))
107 }
108}
109
110impl TryFrom<u32> for Variable {
111 type Error = VariableError;
112 fn try_from(value: u32) -> Result<Self, Self::Error> {
113 Ok(Variable(TypeValue::var_integer_from(value)))
114 }
115}
116
117impl TryFrom<u16> for Variable {
118 type Error = VariableError;
119 fn try_from(value: u16) -> Result<Self, Self::Error> {
120 Ok(Variable(TypeValue::var_integer_from(value)))
121 }
122}
123
124impl TryFrom<u8> for Variable {
125 type Error = VariableError;
126 fn try_from(value: u8) -> Result<Self, Self::Error> {
127 Ok(Variable(TypeValue::var_integer_from(value)))
128 }
129}
130
131impl TryFrom<f64> for Variable {
132 type Error = VariableError;
133 fn try_from(value: f64) -> Result<Self, Self::Error> {
134 Ok(Variable(TypeValue::var_float_from(value)))
135 }
136}
137
138impl TryFrom<f32> for Variable {
139 type Error = VariableError;
140 fn try_from(value: f32) -> Result<Self, Self::Error> {
141 Ok(Variable(TypeValue::var_float_from(value)))
142 }
143}
144
145impl TryFrom<&str> for Variable {
146 type Error = VariableError;
147 fn try_from(value: &str) -> Result<Self, Self::Error> {
148 Ok(Variable(TypeValue::var_string_from(value)))
149 }
150}
151
152impl TryFrom<&[u8]> for Variable {
153 type Error = VariableError;
154 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
155 Ok(Variable(TypeValue::var_string_from(value)))
156 }
157}
158
159impl TryFrom<String> for Variable {
160 type Error = VariableError;
161 fn try_from(value: String) -> Result<Self, Self::Error> {
162 Ok(Variable(TypeValue::var_string_from(value)))
163 }
164}
165
166impl TryFrom<serde_json::Value> for Variable {
167 type Error = VariableError;
168 fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
169 Variable::try_from(&value)
170 }
171}
172
173impl TryFrom<&serde_json::Value> for Variable {
174 type Error = VariableError;
175 fn try_from(value: &serde_json::Value) -> Result<Self, Self::Error> {
176 match value {
177 serde_json::Value::Null => Err(VariableError::UnexpectedNull),
178 serde_json::Value::Bool(b) => {
179 Ok(Variable(TypeValue::var_bool_from(*b)))
180 }
181 serde_json::Value::Number(n) => {
182 if let Some(n) = n.as_u64() {
183 let n: i64 = n
184 .try_into()
185 .map_err(|_| VariableError::IntegerOutOfRange)?;
186 Ok(Variable(TypeValue::var_integer_from(n)))
187 } else if let Some(n) = n.as_i64() {
188 Ok(Variable(TypeValue::var_integer_from(n)))
189 } else if let Some(n) = n.as_f64() {
190 Ok(Variable(TypeValue::var_float_from(n)))
191 } else {
192 unreachable!()
193 }
194 }
195 serde_json::Value::String(s) => {
196 Ok(Variable(TypeValue::var_string_from(s)))
197 }
198 serde_json::Value::Array(values) => {
199 let mut array = None;
200 for v in values {
203 if v.is_boolean() {
204 array = Some(Array::Bools(Vec::new()));
205 break;
206 } else if v.is_i64() {
207 array = Some(Array::Integers(Vec::new()));
208 break;
209 } else if v.is_f64() {
210 array = Some(Array::Floats(Vec::new()));
211 break;
212 } else if v.is_string() {
213 array = Some(Array::Strings(Vec::new()));
214 break;
215 } else if v.is_object() {
216 array = Some(Array::Structs(Vec::new()));
217 break;
218 } else if v.is_array() {
219 return Err(VariableError::InvalidArray);
221 }
222 }
223
224 if array.is_none() {
227 return Err(VariableError::InvalidArray);
228 }
229
230 let mut array = array.unwrap();
231
232 match array {
233 Array::Integers(ref mut integers) => {
234 for v in values {
235 match v.as_i64() {
236 Some(v) => {
237 integers.push(v);
238 }
239 None => {
240 return Err(VariableError::InvalidArray);
241 }
242 };
243 }
244 }
245 Array::Floats(ref mut floats) => {
246 for v in values {
247 match v.as_f64() {
248 Some(v) => {
249 floats.push(v);
250 }
251 None => {
252 return Err(VariableError::InvalidArray);
253 }
254 };
255 }
256 }
257 Array::Bools(ref mut bools) => {
258 for v in values {
259 match v.as_bool() {
260 Some(v) => {
261 bools.push(v);
262 }
263 None => {
264 return Err(VariableError::InvalidArray);
265 }
266 };
267 }
268 }
269 Array::Strings(ref mut strings) => {
270 for v in values {
271 match v.as_str() {
272 Some(v) => {
273 strings.push(BString::from(v).into());
274 }
275 None => {
276 return Err(VariableError::InvalidArray);
277 }
278 };
279 }
280 }
281 Array::Structs(ref mut structs) => {
282 for v in values {
283 match v.as_object() {
284 Some(v) => {
285 let mut s = types::Struct::new();
286 for (key, value) in v {
287 s.add_field(
288 key,
289 TypeValue::from(
290 Variable::try_from(value)?,
291 ),
292 );
293 }
294 structs.push(Rc::new(s));
295 }
296 None => {
297 return Err(VariableError::InvalidArray);
298 }
299 };
300 }
301 }
302 }
303 Ok(Variable(TypeValue::Array(Rc::new(array))))
304 }
305 serde_json::Value::Object(obj) => {
306 let mut s = types::Struct::new();
307 for (key, value) in obj {
308 if !is_valid_identifier(key) {
309 return Err(VariableError::InvalidIdentifier(
310 key.to_string(),
311 ));
312 }
313 s.add_field(
314 key,
315 TypeValue::from(Variable::try_from(value)?),
316 );
317 }
318 Ok(Variable(TypeValue::Struct(Rc::new(s))))
319 }
320 }
321 }
322}
323
324impl From<Variable> for TypeValue {
325 fn from(value: Variable) -> Self {
326 value.0
327 }
328}
329
330pub(crate) fn is_valid_identifier(ident: &str) -> bool {
335 let mut chars = ident.chars();
336
337 if let Some(first) = chars.next() {
338 if !first.is_alphabetic() && first != '_' {
340 return false;
341 }
342 } else {
343 return false;
345 }
346
347 chars.all(|c| c.is_alphanumeric() || c == '_')
349}
350
351#[cfg(test)]
352mod test {
353 #[test]
354 fn is_valid_identifier() {
355 assert!(super::is_valid_identifier("a"));
357 assert!(super::is_valid_identifier("_"));
358 assert!(super::is_valid_identifier("foo"));
359 assert!(super::is_valid_identifier("_foo"));
360
361 assert!(!super::is_valid_identifier("123"));
363 assert!(!super::is_valid_identifier("1foo"));
364 assert!(!super::is_valid_identifier("foo|"));
365 assert!(!super::is_valid_identifier("foo.bar"));
366 }
367}