serde_hkx/xml/de/
map.rs

1//! Deserializing each field element in an `Struct`
2
3use crate::{
4    tri,
5    xml::de::parser::{delimited_multispace0_comment, delimited_with_multispace0},
6};
7
8use super::{
9    XmlDeserializer,
10    parser::tag::{end_tag, field_start_close_tag, field_start_open_tag},
11};
12use crate::errors::de::{Error, Result};
13use havok_serde::de::{DeserializeSeed, MapAccess};
14use havok_types::Pointer;
15use winnow::{Parser as _, combinator::alt, token::take_until};
16
17/// A structure for deserializing each element in an `Struct`.
18///
19/// # Expected XML
20/// ```xml
21/// <hkobject name="#0010" class="hkbProjectData" signature="0x13a39ba7"> <!-- <-Parsed by `deserialize_struct` -->
22///   <!-- memSizeAndFlags SERIALIZE_IGNORED -->
23///   <!-- referenceCount SERIALIZE_IGNORED -->
24///   <hkparam name="worldUpWS">(0.000000 0.000000 1.000000 0.000000)</hkparam>
25///   <hkparam name="stringData">#0009</hkparam>
26///   <hkparam name="defaultEventMode">EVENT_MODE_IGNORE_FROM_GENERATOR</hkparam>
27/// </hkobject>
28/// ```
29#[derive(Debug)]
30pub struct MapDeserializer<'a, 'de: 'a> {
31    /// Deserializer
32    de: &'a mut XmlDeserializer<'de>,
33    ptr_name: Option<Pointer>,
34    class_name: &'static str,
35}
36
37impl<'a, 'de> MapDeserializer<'a, 'de> {
38    /// Create a new map deserializer
39    #[inline]
40    pub const fn new(
41        de: &'a mut XmlDeserializer<'de>,
42        ptr_name: Option<Pointer>,
43        class_name: &'static str,
44    ) -> Self {
45        Self {
46            de,
47            ptr_name,
48            class_name,
49        }
50    }
51}
52
53impl<'de> MapAccess<'de> for MapDeserializer<'_, 'de> {
54    type Error = Error;
55
56    #[inline]
57    fn class_ptr(&mut self) -> Option<Pointer> {
58        self.ptr_name.take()
59    }
60
61    // Parse e.g. `<hkparam name="worldUpWS"`, `<hkparam name="boneWeights"`
62    fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
63    where
64        K: DeserializeSeed<'de>,
65    {
66        // Check if there are no more elements.
67        if self.de.parse_peek(end_tag("hkobject")).is_ok() {
68            return Ok(None);
69        }
70        // Avoid infinite loops by checking the end of XML.
71        if self.de.input.is_empty() {
72            return Err(Error::Eof);
73        }
74
75        tri!(self.de.parse_next(field_start_open_tag(self.class_name))); // Parse `<hkparam name=`
76        seed.deserialize(&mut *self.de).map(Some) // Parse `"field_name"`
77    }
78
79    // Parse e.g. `>(0.000000 0.000000 1.000000 0.000000)</hkparam>` or `numelements="0" />`
80    fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
81    where
82        V: DeserializeSeed<'de>,
83    {
84        // HACK: If the `strict` feature is disabled, fall back to the default value
85        // if there is an error retrieving the value in the `havok_classes` crate, so check
86        // for `>` here and omit the implementation of the `/>` shorthand notation.
87        let (_len, is_self_closing) = tri!(self.de.parse_next(field_start_close_tag)); // Parse `>`, `/>`, or ` numelements="3">`
88        #[cfg(feature = "tracing")]
89        if let Some(numelements) = _len {
90            tracing::debug!(numelements);
91        }
92
93        if is_self_closing {
94            // For self-closing tags, we need to provide a default value
95            // Try to deserialize with an empty string or let the deserializer handle the default
96            const EMPTY_INPUT: &str = "";
97            let original_input = self.de.input;
98            self.de.input = EMPTY_INPUT;
99            let result = seed.deserialize(&mut *self.de);
100            self.de.input = original_input;
101            result
102        } else {
103            let value = tri!(seed.deserialize(&mut *self.de));
104            tri!(self.de.parse_next(end_tag("hkparam")));
105            Ok(value)
106        }
107    }
108
109    // If an unknown `<hkparam>` exists and `next_value` is not called , `/>` or `</hkparam>` must be skipped.
110    fn skip_value_seed(&mut self) -> std::result::Result<(), Self::Error> {
111        let ((_num, _is_self_closing), _value) = tri!(
112            self.de.parse_next(alt((
113                winnow::seq! {
114                    field_start_close_tag, // Parse `>`, `/>`, or ` numelements="3">`
115                    take_until(0.., "<"),    // take any value
116                    _: end_tag("hkparam")       // </hkparam>
117                },
118                winnow::seq! { // Self closing tag (legacy handling - now handled in field_start_close_tag)
119                    _: delimited_with_multispace0("/"),
120                    _: delimited_multispace0_comment(">")
121                }
122                .map(|_| ((None, true), "")),
123            )))
124        );
125        #[cfg(feature = "tracing")]
126        {
127            let numelements = _num
128                .map(|n| format!("numelements=\"{n}\""))
129                .unwrap_or_default();
130            tracing::debug!("`Skip `{numelements}>{_value}</hkparam>`.");
131        }
132        Ok(())
133    }
134
135    #[cold]
136    fn parent_value_seed<V>(&mut self, _seed: V) -> Result<V::Value, Self::Error>
137    where
138        V: DeserializeSeed<'de>,
139    {
140        unreachable!(
141            "Using the wrong API: This method is not used in `havok_classes` in XML because it is for bytes."
142        )
143    }
144}