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