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