Skip to main content

fmi_schema/fmi3/variable/
dimension.rs

1//! Custom implementations of YaSerialize and YaDeserialize for Dimension
2use hard_xml::{XmlRead, XmlWrite};
3
4#[derive(Clone, PartialEq, Debug)]
5pub enum Dimension {
6    /// Defines a constant unsigned 64-bit integer size for this dimension. The variability of the
7    /// dimension size is constant in this case.
8    Fixed(u64),
9    /// If the present, it defines the size of this dimension to be the value of the variable with
10    /// the value reference given by the `value_reference` attribute. The referenced variable
11    /// must be a variable of type `UInt64`, and must either be a constant (i.e. with
12    /// variability = constant) or a structural parameter (i.e. with causality =
13    /// structuralParameter). The variability of the dimension size is in this case the variability
14    /// of the referenced variable. A structural parameter must be a variable of type `UInt64`
15    /// only if it is referenced in `Dimension`.
16    Variable(u32),
17}
18
19impl Dimension {
20    pub fn as_fixed(&self) -> Option<u64> {
21        match self {
22            Dimension::Fixed(size) => Some(*size),
23            Dimension::Variable(_) => None,
24        }
25    }
26
27    pub fn as_variable(&self) -> Option<u32> {
28        match self {
29            Dimension::Fixed(_) => None,
30            Dimension::Variable(value_reference) => Some(*value_reference),
31        }
32    }
33}
34
35impl Default for Dimension {
36    fn default() -> Self {
37        Self::Fixed(1)
38    }
39}
40
41#[derive(hard_xml::XmlRead, hard_xml::XmlWrite)]
42#[xml(tag = "Dimension")]
43struct Inner {
44    #[xml(attr = "start")]
45    start: Option<u64>,
46    #[xml(attr = "valueReference")]
47    value_reference: Option<u32>,
48}
49
50impl<'a> XmlRead<'a> for Dimension {
51    fn from_reader(reader: &mut hard_xml::XmlReader<'a>) -> hard_xml::XmlResult<Self> {
52        let Inner {
53            start,
54            value_reference,
55        } = Inner::from_reader(reader)?;
56
57        match (start, value_reference) {
58            (Some(_), Some(_)) => Err(hard_xml::XmlExtendedError::DuplicateAttribute(
59                "Dimension cannot have both 'start' and 'valueReference' attributes".into(),
60            )
61            .into()),
62            (None, None) => Err(hard_xml::XmlError::MissingField {
63                name: "Dimension".into(),
64                field: "either 'start' or 'valueReference'".into(),
65            }),
66            (Some(start), None) => Ok(Dimension::Fixed(start)),
67            (None, Some(value_reference)) => Ok(Dimension::Variable(value_reference)),
68        }
69    }
70}
71
72impl XmlWrite for Dimension {
73    fn to_writer<W: std::io::Write>(
74        &self,
75        writer: &mut hard_xml::XmlWriter<W>,
76    ) -> hard_xml::XmlResult<()> {
77        match self {
78            Dimension::Fixed(fixed) => Inner {
79                start: Some(*fixed),
80                value_reference: None,
81            },
82            Dimension::Variable(variable) => Inner {
83                start: None,
84                value_reference: Some(*variable),
85            },
86        }
87        .to_writer(writer)
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[derive(Default, PartialEq, Debug, hard_xml::XmlRead, hard_xml::XmlWrite)]
96    #[xml(tag = "TestVar")]
97    pub struct TestVar {
98        #[xml(child = "Dimension")]
99        pub dimensions: Vec<Dimension>,
100    }
101    #[test]
102    fn test_dim_de() {
103        let _ = env_logger::builder()
104            .is_test(true)
105            .format_timestamp(None)
106            .try_init();
107
108        let xml = r#"<TestVar>
109    <Dimension valueReference="2"/>
110    <Dimension start="2"/>
111    </TestVar>"#;
112        let var = TestVar::from_str(xml).unwrap();
113
114        assert_eq!(var.dimensions.len(), 2);
115        assert_eq!(var.dimensions[0], Dimension::Variable(2));
116        assert_eq!(var.dimensions[1], Dimension::Fixed(2));
117    }
118
119    #[test]
120    fn test_dim_roundtrip() {
121        let var = TestVar {
122            dimensions: vec![Dimension::Fixed(2), Dimension::Variable(0)],
123        };
124        let serialized = var.to_string().unwrap();
125        let deserialized = TestVar::from_str(&serialized).unwrap();
126        assert_eq!(var, deserialized);
127    }
128}