arma_rs/value/
from_arma.rs

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/// Error type for [`FromArma`]
29#[derive(Debug, PartialEq, Eq)]
30pub enum FromArmaError {
31    /// Invalid [`crate::Value`]
32    InvalidValue(String),
33    /// Invalid primitive value
34    InvalidPrimitive(String),
35    /// Collection size mismatch
36    InvalidLength {
37        /// Expected size
38        expected: usize,
39        /// Actual size
40        actual: usize,
41    },
42
43    /// Missing opening(true) or closing(false) bracket
44    MissingBracket(bool),
45    /// Missing field
46    MissingField(String),
47    /// Unknown field
48    UnknownField(String),
49    /// Duplicate field
50    DuplicateField(String),
51
52    /// Custom error message
53    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    /// Creates a new [`FromArmaError::Custom`]
78    pub fn custom(msg: impl std::fmt::Display) -> Self {
79        Self::Custom(msg.to_string())
80    }
81}
82
83/// A trait for converting a value from Arma to a Rust value.
84pub trait FromArma: Sized {
85    /// Converts a value from Arma to a Rust value.
86    /// # Errors
87    /// Will return an error if the value cannot be converted.
88    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                        // parse exponential notation
136                        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}