1use crate::Value;
2
3fn split_array(s: &str) -> Vec<String> {
4 let mut nest = 0;
5 let mut parts = Vec::new();
6 let mut part = String::new();
7 for c in s.chars() {
8 if c == '[' {
9 part.push(c);
10 nest += 1;
11 } else if c == ']' {
12 nest -= 1;
13 part.push(c);
14 } else if c == ',' && nest == 0 {
15 parts.push(part.trim().to_string());
16 part = String::new();
17 } else {
18 part.push(c);
19 }
20 }
21 let part = part.trim().to_string();
22 if !part.is_empty() {
23 parts.push(part);
24 }
25 parts
26}
27
28#[derive(Debug, PartialEq, Eq)]
30pub enum FromArmaError {
31 InvalidValue(String),
33 InvalidPrimitive(String),
35 InvalidLength {
37 expected: usize,
39 actual: usize,
41 },
42
43 MissingBracket(bool),
45 MissingField(String),
47 UnknownField(String),
49 DuplicateField(String),
51
52 Custom(String),
54}
55
56impl std::fmt::Display for FromArmaError {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 match self {
59 Self::InvalidValue(s) => write!(f, "invalid value: {s}"),
60 Self::InvalidPrimitive(s) => write!(f, "error parsing primitive: {s}"),
61 Self::InvalidLength { expected, actual } => {
62 write!(f, "expected {expected} elements, got {actual}")
63 }
64 Self::MissingBracket(start) => {
65 if *start {
66 write!(f, "missing '[' at start of array")
67 } else {
68 write!(f, "missing ']' at end of array")
69 }
70 }
71 Self::MissingField(s) => write!(f, "missing field: {s}"),
72 Self::UnknownField(s) => write!(f, "unknown field: {s}"),
73 Self::DuplicateField(s) => write!(f, "duplicate field: {s}"),
74 Self::Custom(s) => f.write_str(s),
75 }
76 }
77}
78
79impl FromArmaError {
80 pub fn custom(msg: impl std::fmt::Display) -> Self {
82 Self::Custom(msg.to_string())
83 }
84}
85
86pub trait FromArma: Sized {
88 fn from_arma(s: String) -> Result<Self, FromArmaError>;
92}
93
94#[cfg(not(any(test, doc, debug_assertions)))]
95impl FromArma for String {
96 fn from_arma(s: String) -> Result<Self, FromArmaError> {
97 let Some(s) = s.strip_prefix('"').and_then(|s| s.strip_suffix('"')) else {
98 return Err(FromArmaError::InvalidPrimitive(String::from(
99 "missing '\"' at start or end of string",
100 )));
101 };
102 Ok(s.replace("\"\"", "\""))
103 }
104}
105
106#[cfg(any(test, doc, debug_assertions))]
107impl FromArma for String {
108 fn from_arma(s: String) -> Result<Self, FromArmaError> {
109 let s = s
110 .strip_prefix('"')
111 .and_then(|s| s.strip_suffix('"'))
112 .unwrap_or(&s);
113 Ok(s.replace("\"\"", "\""))
114 }
115}
116
117macro_rules! impl_from_arma {
118 ($($t:ty),*) => {
119 $(
120 impl FromArma for $t {
121 fn from_arma(s: String) -> Result<Self, FromArmaError> {
122 let s = s.strip_suffix('"').and_then(|s| s.strip_prefix('"')).unwrap_or(&s);
123 s.parse::<Self>().map_err(|e| FromArmaError::InvalidPrimitive(e.to_string()))
124 }
125 }
126 )*
127 };
128}
129impl_from_arma!(f32, f64, bool, char);
130
131macro_rules! impl_from_arma_number {
132 ($($t:ty),*) => {
133 $(
134 impl FromArma for $t {
135 fn from_arma(s: String) -> Result<Self, FromArmaError> {
136 let s = s.strip_suffix('"').and_then(|s| s.strip_prefix('"')).unwrap_or(&s);
137 if s.contains("e") {
138 let mut parts = s.split('e');
140 let base = match parts.next().unwrap() {
141 s if !s.is_empty() => s.parse::<f64>().map_err(|e| FromArmaError::InvalidPrimitive(e.to_string()))?,
142 _ => return Err(FromArmaError::InvalidPrimitive("invalid number literal".to_string())),
143 };
144 let exp = match parts.next().unwrap() {
145 s if !s.is_empty() => s.parse::<i32>().map_err(|e| FromArmaError::InvalidPrimitive(e.to_string()))?,
146 _ => return Err(FromArmaError::InvalidPrimitive("invalid number literal".to_string())),
147 };
148 return Ok((base * 10.0_f64.powi(exp)) as $t);
149 }
150 s.parse::<Self>().map_err(|e| FromArmaError::InvalidPrimitive(e.to_string()))
151 }
152 }
153 )*
154 };
155}
156impl_from_arma_number!(
157 i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize
158);
159
160macro_rules! impl_from_arma_tuple {
161 { $c: expr, $($t:ident)* } => {
162 impl<$($t),*> FromArma for ($($t),*)
163 where
164 $($t: FromArma),*
165 {
166 fn from_arma(s: String) -> Result<Self, FromArmaError> {
167 let v: [Value; $c] = FromArma::from_arma(s)?;
168 let mut iter = v.iter();
169 Ok((
170 $($t::from_arma(iter.next().unwrap().to_string())?),*
171 ))
172 }
173 }
174 };
175}
176
177impl_from_arma_tuple! { 2, A B }
178impl_from_arma_tuple! { 3, A B C }
179impl_from_arma_tuple! { 4, A B C D }
180impl_from_arma_tuple! { 5, A B C D E }
181impl_from_arma_tuple! { 6, A B C D E F }
182impl_from_arma_tuple! { 7, A B C D E F G }
183impl_from_arma_tuple! { 8, A B C D E F G H }
184impl_from_arma_tuple! { 9, A B C D E F G H I }
185impl_from_arma_tuple! { 10, A B C D E F G H I J }
186impl_from_arma_tuple! { 11, A B C D E F G H I J K }
187impl_from_arma_tuple! { 12, A B C D E F G H I J K L }
188impl_from_arma_tuple! { 13, A B C D E F G H I J K L M }
189impl_from_arma_tuple! { 14, A B C D E F G H I J K L M N }
190impl_from_arma_tuple! { 15, A B C D E F G H I J K L M N O }
191impl_from_arma_tuple! { 16, A B C D E F G H I J K L M N O P }
192impl_from_arma_tuple! { 17, A B C D E F G H I J K L M N O P Q }
193impl_from_arma_tuple! { 18, A B C D E F G H I J K L M N O P Q R }
194impl_from_arma_tuple! { 19, A B C D E F G H I J K L M N O P Q R S }
195impl_from_arma_tuple! { 20, A B C D E F G H I J K L M N O P Q R S T }
196impl_from_arma_tuple! { 21, A B C D E F G H I J K L M N O P Q R S T U }
197impl_from_arma_tuple! { 22, A B C D E F G H I J K L M N O P Q R S T U V }
198impl_from_arma_tuple! { 23, A B C D E F G H I J K L M N O P Q R S T U V W }
199impl_from_arma_tuple! { 24, A B C D E F G H I J K L M N O P Q R S T U V W X }
200impl_from_arma_tuple! { 25, A B C D E F G H I J K L M N O P Q R S T U V W X Y }
201impl_from_arma_tuple! { 26, A B C D E F G H I J K L M N O P Q R S T U V W X Y Z }
202
203impl<T> FromArma for Vec<T>
204where
205 T: FromArma,
206{
207 fn from_arma(s: String) -> Result<Self, FromArmaError> {
208 let source = s
209 .strip_prefix('[')
210 .ok_or(FromArmaError::MissingBracket(true))?
211 .strip_suffix(']')
212 .ok_or(FromArmaError::MissingBracket(false))?;
213 let parts = split_array(source);
214 parts.iter().try_fold(Self::new(), |mut acc, p| {
215 acc.push(T::from_arma(p.clone())?);
216 Ok(acc)
217 })
218 }
219}
220
221impl<T, const N: usize> FromArma for [T; N]
222where
223 T: FromArma,
224{
225 fn from_arma(s: String) -> Result<Self, FromArmaError> {
226 let v: Vec<T> = FromArma::from_arma(s)?;
227 let len = v.len();
228 v.try_into().map_err(|_| FromArmaError::InvalidLength {
229 expected: N,
230 actual: len,
231 })
232 }
233}
234
235impl<K, V, S> FromArma for std::collections::HashMap<K, V, S>
236where
237 K: FromArma + Eq + std::hash::Hash,
238 V: FromArma,
239 S: std::hash::BuildHasher + Default,
240{
241 fn from_arma(s: String) -> Result<Self, FromArmaError> {
242 let data: Vec<(K, V)> = FromArma::from_arma(s)?;
243 let mut ret = Self::default();
244 for (k, v) in data {
245 ret.insert(k, v);
246 }
247 Ok(ret)
248 }
249}
250
251#[cfg(test)]
252mod tests {
253 use super::*;
254 use crate::Value;
255
256 #[test]
257 fn parse_tuple_varying_types() {
258 assert_eq!(
259 (String::from("hello"), 123),
260 <(String, i32)>::from_arma(r#"["hello", 123]"#.to_string()).unwrap()
261 );
262 assert!(<(String, String)>::from_arma(r#"["hello", 123, "world"]"#.to_string()).is_err());
263 }
264
265 #[test]
266 fn parse_tuple_size_errors() {
267 assert_eq!(
268 <(String, i32)>::from_arma(r"[]".to_string()),
269 Err(FromArmaError::InvalidLength {
270 expected: 2,
271 actual: 0
272 })
273 );
274 assert_eq!(
275 <(String, i32)>::from_arma(r#"["hello"]"#.to_string()),
276 Err(FromArmaError::InvalidLength {
277 expected: 2,
278 actual: 1
279 })
280 );
281 assert_eq!(
282 <(String, i32)>::from_arma(r#"["hello", 123, 456]"#.to_string()),
283 Err(FromArmaError::InvalidLength {
284 expected: 2,
285 actual: 3
286 })
287 );
288 }
289
290 #[test]
291 fn parse_tuple_bracket_errors() {
292 assert_eq!(
293 <(String, i32)>::from_arma(r#"["hello", 123"#.to_string()),
294 Err(FromArmaError::MissingBracket(false))
295 );
296 assert_eq!(
297 <(String, i32)>::from_arma(r#""hello", 123"#.to_string()),
298 Err(FromArmaError::MissingBracket(true))
299 );
300 }
301
302 #[test]
303 fn test_tuple_2() {
304 assert_eq!(
305 (0, 1),
306 <(u8, u8)>::from_arma(r"[0, 1]".to_string()).unwrap()
307 );
308 }
309
310 #[test]
311 fn test_tuple_3() {
312 assert_eq!(
313 (0, 1, 2),
314 <(u8, u8, u8)>::from_arma(r"[0, 1, 2]".to_string()).unwrap()
315 );
316 }
317
318 #[test]
319 fn test_tuple_4() {
320 assert_eq!(
321 (0, 1, 2, 3),
322 <(u8, u8, u8, u8)>::from_arma(r"[0, 1, 2, 3]".to_string()).unwrap()
323 );
324 }
325
326 #[test]
327 fn test_tuple_5() {
328 assert_eq!(
329 (0, 1, 2, 3, 4),
330 <(u8, u8, u8, u8, u8)>::from_arma(r"[0, 1, 2, 3, 4]".to_string()).unwrap()
331 );
332 }
333
334 #[test]
335 fn test_tuple_6() {
336 assert_eq!(
337 (0, 1, 2, 3, 4, 5),
338 <(u8, u8, u8, u8, u8, u8)>::from_arma(r"[0, 1, 2, 3, 4, 5]".to_string()).unwrap()
339 );
340 }
341
342 #[test]
343 fn test_tuple_7() {
344 assert_eq!(
345 (0, 1, 2, 3, 4, 5, 6),
346 <(u8, u8, u8, u8, u8, u8, u8)>::from_arma(r"[0, 1, 2, 3, 4, 5, 6]".to_string())
347 .unwrap()
348 );
349 }
350
351 #[test]
352 fn test_tuple_8() {
353 assert_eq!(
354 (0, 1, 2, 3, 4, 5, 6, 7),
355 <(u8, u8, u8, u8, u8, u8, u8, u8)>::from_arma(r"[0, 1, 2, 3, 4, 5, 6, 7]".to_string())
356 .unwrap()
357 );
358 }
359
360 #[test]
361 fn test_tuple_9() {
362 assert_eq!(
363 (0, 1, 2, 3, 4, 5, 6, 7, 8),
364 <(u8, u8, u8, u8, u8, u8, u8, u8, u8)>::from_arma(
365 r"[0, 1, 2, 3, 4, 5, 6, 7, 8]".to_string()
366 )
367 .unwrap()
368 );
369 }
370
371 #[test]
372 fn test_tuple_10() {
373 assert_eq!(
374 (0, 1, 2, 3, 4, 5, 6, 7, 8, 9),
375 <(u8, u8, u8, u8, u8, u8, u8, u8, u8, u8)>::from_arma(
376 r"[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]".to_string()
377 )
378 .unwrap()
379 );
380 }
381
382 #[test]
383 fn parse_string() {
384 assert_eq!(
385 String::from("hello"),
386 <String>::from_arma(r#""hello""#.to_string()).unwrap()
387 );
388 assert_eq!(
389 String::from("\"hello\""),
390 <String>::from_arma(r#"""hello"""#.to_string()).unwrap()
391 );
392 assert_eq!(
393 String::from(r#"hello "john"."#),
394 <String>::from_arma(r#""hello ""john"".""#.to_string()).unwrap()
395 );
396 }
397
398 #[test]
399 fn parse_vec() {
400 assert_eq!(
401 vec![String::from("hello"), String::from("bye"),],
402 <Vec<String>>::from_arma(r#"["hello","bye"]"#.to_string()).unwrap()
403 );
404 assert_eq!(
405 vec![String::from("hello"), String::from("world")],
406 <Vec<String>>::from_arma(r#"[hello, "world"]"#.to_string()).unwrap()
407 );
408 }
409
410 #[test]
411 fn parse_vec_bracket_errors() {
412 assert_eq!(
413 <Vec<String>>::from_arma(r#""hello","bye"]"#.to_string()),
414 Err(FromArmaError::MissingBracket(true))
415 );
416 assert_eq!(
417 <Vec<String>>::from_arma(r#"["hello","bye""#.to_string()),
418 Err(FromArmaError::MissingBracket(false))
419 );
420 }
421
422 #[test]
423 fn parse_vec_tuple() {
424 assert_eq!(
425 (vec![(String::from("hello"), 123), (String::from("bye"), 321),]),
426 <Vec<(String, i32)>>::from_arma(r#"[["hello", 123],["bye", 321]]"#.to_string())
427 .unwrap()
428 );
429 }
430
431 #[test]
432 fn parse_slice() {
433 assert_eq!(
434 vec![String::from("hello"), String::from("bye"),],
435 <[String; 2]>::from_arma(r#"["hello","bye"]"#.to_string()).unwrap()
436 );
437 }
438
439 #[test]
440 fn parse_slice_size_errors() {
441 assert_eq!(
442 <[String; 2]>::from_arma(r"[]".to_string()),
443 Err(FromArmaError::InvalidLength {
444 expected: 2,
445 actual: 0
446 })
447 );
448 assert_eq!(
449 <[String; 2]>::from_arma(r#"["hello"]"#.to_string()),
450 Err(FromArmaError::InvalidLength {
451 expected: 2,
452 actual: 1
453 })
454 );
455 assert_eq!(
456 <[String; 2]>::from_arma(r#"["hello","bye","world"]"#.to_string()),
457 Err(FromArmaError::InvalidLength {
458 expected: 2,
459 actual: 3
460 })
461 );
462 }
463
464 #[test]
465 fn parse_hashmap() {
466 assert_eq!(
467 std::collections::HashMap::from([
468 (String::from("hello"), 123),
469 (String::from("bye"), 321),
470 ]),
471 <std::collections::HashMap<String, i32>>::from_arma(
472 r#"[["hello", 123],["bye",321]]"#.to_string()
473 )
474 .unwrap()
475 );
476
477 assert_eq!(
478 std::collections::HashMap::from([
479 (String::from("hello"), 123),
480 (String::from("bye"), 321),
481 (String::from("hello"), 321),
482 ]),
483 <std::collections::HashMap<String, i32>>::from_arma(
484 r#"[["hello", 123],["bye",321],["hello", 321]]"#.to_string()
485 )
486 .unwrap()
487 );
488 }
489
490 #[test]
491 fn parse_exponential() {
492 assert_eq!(1.0e-10, <f64>::from_arma(r"1.0e-10".to_string()).unwrap());
493 assert_eq!(
494 1_227_700,
495 <u32>::from_arma(r"1.2277e+006".to_string()).unwrap()
496 );
497 }
498
499 #[test]
500 fn parse_exponential_errors() {
501 assert_eq!(
502 <f64>::from_arma(r"e-10".to_string()),
503 Err(FromArmaError::InvalidPrimitive(
504 "invalid float literal".to_string()
505 ))
506 );
507 assert_eq!(
508 <f64>::from_arma(r"1.0e".to_string()),
509 Err(FromArmaError::InvalidPrimitive(
510 "invalid float literal".to_string()
511 ))
512 );
513
514 assert_eq!(
515 <u32>::from_arma(r"e-10".to_string()),
516 Err(FromArmaError::InvalidPrimitive(
517 "invalid number literal".to_string()
518 ))
519 );
520 assert_eq!(
521 <u32>::from_arma(r"1.0e".to_string()),
522 Err(FromArmaError::InvalidPrimitive(
523 "invalid number literal".to_string()
524 ))
525 );
526 }
527
528 #[test]
529 fn parse_value_tuple() {
530 assert_eq!(
531 (
532 Value::String(String::from("hello")),
533 Value::String(String::from("world"))
534 ),
535 <(Value, Value)>::from_arma(r#"["hello", "world"]"#.to_string()).unwrap()
536 );
537 }
538
539 #[test]
540 fn parse_value_vec() {
541 assert_eq!(
542 vec![
543 Value::String(String::from("hello")),
544 Value::String(String::from("world"))
545 ],
546 <Vec<Value>>::from_arma(r#"["hello", "world"]"#.to_string()).unwrap()
547 );
548 }
549
550 #[test]
551 fn parse_float() {
552 assert_eq!(1.0, <f64>::from_arma(r#"1.0"#.to_string()).unwrap());
553 assert_eq!(1.0, <f64>::from_arma(r#"1"#.to_string()).unwrap());
554 assert_eq!(1.0, <f64>::from_arma(r#"1.0e+0"#.to_string()).unwrap());
555 assert_eq!(-1.0, <f64>::from_arma(r#"-1.0"#.to_string()).unwrap());
556 assert_eq!(1.0, <f64>::from_arma(r#""1.0""#.to_string()).unwrap());
557 assert_eq!(1.0, <f64>::from_arma(r#""1""#.to_string()).unwrap());
558 assert_eq!(1.0, <f64>::from_arma(r#""1.0e+0""#.to_string()).unwrap());
559 assert_eq!(-1.0, <f64>::from_arma(r#""-1.0""#.to_string()).unwrap());
560 }
561}