Skip to main content

fmi_schema/
utils.rs

1//! Utility functions for serde deserialization
2
3use std::{fmt::Display, ops::Deref, str::FromStr};
4
5/// Custom deserializer for `Optional<f64>` that can handle string inputs from JSON
6#[cfg(feature = "serde")]
7pub fn deserialize_optional_f64_from_string<'de, D>(
8    deserializer: D,
9) -> Result<Option<f64>, D::Error>
10where
11    D: serde::Deserializer<'de>,
12{
13    use serde::Deserialize;
14    use serde::de::Error;
15
16    #[derive(Deserialize)]
17    #[serde(untagged)]
18    enum StringOrF64 {
19        String(String),
20        F64(f64),
21    }
22
23    let value = Option::<StringOrF64>::deserialize(deserializer)?;
24
25    match value {
26        Some(StringOrF64::String(s)) => s
27            .parse::<f64>()
28            .map(Some)
29            .map_err(|_| D::Error::custom(format!("Invalid number format: '{}'", s))),
30        Some(StringOrF64::F64(f)) => Ok(Some(f)),
31        None => Ok(None),
32    }
33}
34
35/// Newtype for space-separated lists in XML attributes
36#[derive(PartialEq, Debug)]
37pub struct AttrList<T>(pub Vec<T>);
38
39impl<T> Deref for AttrList<T> {
40    type Target = [T];
41
42    fn deref(&self) -> &Self::Target {
43        self.0.deref()
44    }
45}
46
47impl<T: FromStr> FromStr for AttrList<T> {
48    type Err = T::Err;
49
50    fn from_str(s: &str) -> Result<Self, Self::Err> {
51        let items = s
52            .split_whitespace()
53            .map(|item| item.parse())
54            .collect::<Result<Vec<T>, T::Err>>()?;
55        Ok(AttrList(items))
56    }
57}
58
59impl<T: Display> Display for AttrList<T> {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        use itertools::Itertools;
62        write!(f, "{}", self.0.iter().join(" "))
63    }
64}