Skip to main content

wolfram_serialize/
from_wxf.rs

1//! [`FromWXF`] — pull-based typed deserialization from a [`WxfReader`].
2//!
3//! Lifetime-parameterized like serde's `Deserialize<'de>`: `'de` is the input
4//! buffer's lifetime. **Owned** types implement `FromWXF<'de>` for *every* `'de`
5//! (they borrow nothing); **borrowed** types (`&'de str`, `&'de [u8]`, and
6//! derived structs with reference fields) tie to a specific `'de` and read
7//! zero-copy straight out of the buffer.
8//!
9//! Peek-free by construction: every value begins with one expression token, read
10//! once via [`WxfReader::read_expr_token`] and threaded into
11//! [`FromWXF::from_wxf_with_tag`].
12
13use std::collections::{BTreeMap, HashMap};
14
15use crate::constants::ExpressionEnum;
16use crate::reader::Reader;
17use crate::wxf::reader::WxfReader;
18use crate::Error;
19
20/// Deserialize a typed value by pulling tokens from a [`WxfReader`].
21///
22/// Implemented by hand for scalars / std types and the `wolfram-expr` value
23/// types, and derivable via `#[derive(FromWXF)]`. Implementors usually provide
24/// only [`from_wxf_with_tag`][FromWXF::from_wxf_with_tag]; the default
25/// [`from_wxf`][FromWXF::from_wxf] reads the leading token and delegates.
26///
27/// `'de` is the lifetime of the input buffer. Owned types are generic over it;
28/// borrowed types (e.g. `&'de str`) name it in `Self`.
29pub trait FromWXF<'de>: Sized {
30    /// Read a complete value: its expression token, then its body.
31    fn from_wxf<R: Reader<'de>>(r: &mut WxfReader<R>) -> Result<Self, Error> {
32        let tok = r.read_expr_token()?;
33        Self::from_wxf_with_tag(r, tok)
34    }
35
36    /// Read the body given the already-consumed expression token.
37    fn from_wxf_with_tag<R: Reader<'de>>(
38        r: &mut WxfReader<R>,
39        tok: ExpressionEnum,
40    ) -> Result<Self, Error>;
41}
42
43/// Build a `Deserialize` error tagged with a path. Used by the derive.
44pub fn err_at(path: impl Into<String>, expected: &'static str, got: String) -> Error {
45    Error::Deserialize {
46        path: path.into(),
47        expected: expected,
48        got: got,
49    }
50}
51
52// FromWXF impls for the `wolfram-expr` value types (Expr, Symbol, Association,
53// NumericArray, PackedArray, BigInteger, BigReal) live in `wolfram-expr`, which
54// depends on this crate.
55
56//==============================================================================
57// Borrowed (zero-copy) primitives
58//==============================================================================
59
60impl<'de> FromWXF<'de> for &'de str {
61    fn from_wxf_with_tag<R: Reader<'de>>(
62        r: &mut WxfReader<R>,
63        tok: ExpressionEnum,
64    ) -> Result<Self, Error> {
65        if tok != ExpressionEnum::String {
66            return Err(Error::unexpected_token(&["String"], tok));
67        }
68        r.read_str()
69    }
70}
71
72impl<'de> FromWXF<'de> for &'de [u8] {
73    fn from_wxf_with_tag<R: Reader<'de>>(
74        r: &mut WxfReader<R>,
75        tok: ExpressionEnum,
76    ) -> Result<Self, Error> {
77        if tok != ExpressionEnum::ByteArray {
78            return Err(Error::unexpected_token(&["ByteArray"], tok));
79        }
80        r.read_byte_array()
81    }
82}
83
84//==============================================================================
85// Primitive scalars (owned — generic over 'de)
86//==============================================================================
87
88// One macro for all numeric scalars. Each call site lists *exactly* the wire
89// tokens that fit in the target type without any runtime range check or silent
90// truncation. Tokens are matched directly to their read method via a helper.
91//
92// Helper: map a wire-token identifier to its WxfReader read call.
93macro_rules! read_wire {
94    (Integer8,  $r:expr) => {
95        $r.read_i8()? as _
96    };
97    (Integer16, $r:expr) => {
98        $r.read_i16()? as _
99    };
100    (Integer32, $r:expr) => {
101        $r.read_i32()? as _
102    };
103    (Integer64, $r:expr) => {
104        $r.read_i64()? as _
105    };
106    (Real64,    $r:expr) => {
107        $r.read_f64()? as _
108    };
109}
110
111macro_rules! impl_numeric_from_wxf {
112    ($t:ty, [$($tok:ident),+]) => {
113        impl<'de> FromWXF<'de> for $t {
114            fn from_wxf_with_tag<R: Reader<'de>>(
115                r: &mut WxfReader<R>,
116                tok: ExpressionEnum,
117            ) -> Result<Self, Error> {
118                match tok {
119                    $(ExpressionEnum::$tok => Ok(read_wire!($tok, r)),)+
120                    other => Err(Error::unexpected_token(
121                        &[$(stringify!($tok)),+],
122                        other,
123                    )),
124                }
125            }
126        }
127    };
128}
129
130// Signed integers: accept only wire tokens whose values always fit (same or
131// smaller width). No runtime range check — the type of the wire read guarantees it.
132impl_numeric_from_wxf!(i8, [Integer8]);
133impl_numeric_from_wxf!(i16, [Integer8, Integer16]);
134impl_numeric_from_wxf!(i32, [Integer8, Integer16, Integer32]);
135impl_numeric_from_wxf!(i64, [Integer8, Integer16, Integer32, Integer64]);
136
137// Floats: accept integer wire tokens whose bit width fits in the mantissa, plus
138// Real64 (the only real wire type — f32 narrows it, unavoidably).
139// f32 mantissa = 23 bits: i8 (7-bit) and i16 (15-bit) fit; i32 (31-bit) does not.
140// f64 mantissa = 52 bits: i8, i16, i32 (31-bit) fit; i64 (63-bit) does not.
141impl_numeric_from_wxf!(f32, [Integer8, Integer16]);
142impl_numeric_from_wxf!(f64, [Integer8, Integer16, Integer32, Real64]);
143
144impl<'de> FromWXF<'de> for bool {
145    fn from_wxf_with_tag<R: Reader<'de>>(
146        r: &mut WxfReader<R>,
147        tok: ExpressionEnum,
148    ) -> Result<Self, Error> {
149        if tok != ExpressionEnum::Symbol {
150            return Err(Error::unexpected_token(&["Symbol"], tok));
151        }
152        match r.read_str()? {
153            "System`True" => Ok(true),
154            "System`False" => Ok(false),
155            other => Err(Error::UnexpectedSymbol {
156                expected: vec!["System`True", "System`False"],
157                got: other.to_owned(),
158            }),
159        }
160    }
161}
162
163// Owned `String` is the borrowed `&str` read + a copy.
164impl<'de> FromWXF<'de> for String {
165    fn from_wxf_with_tag<R: Reader<'de>>(
166        r: &mut WxfReader<R>,
167        tok: ExpressionEnum,
168    ) -> Result<Self, Error> {
169        <&str as FromWXF<'de>>::from_wxf_with_tag(r, tok).map(str::to_owned)
170    }
171}
172
173//==============================================================================
174// Containers
175//==============================================================================
176
177impl<'de> FromWXF<'de> for () {
178    fn from_wxf_with_tag<R: Reader<'de>>(
179        r: &mut WxfReader<R>,
180        tok: ExpressionEnum,
181    ) -> Result<Self, Error> {
182        if tok != ExpressionEnum::Symbol {
183            return Err(Error::unexpected_token(&["Symbol"], tok));
184        }
185        match r.read_str()? {
186            "Null" | "System`Null" => Ok(()),
187            other => Err(Error::UnexpectedSymbol {
188                expected: vec!["Null", "System`Null"],
189                got: other.to_owned(),
190            }),
191        }
192    }
193}
194
195// Option<T> / Result<T, E> read the same enum-association format the derive
196// emits (and that `Option`/`Result` ToWXF writes) — via the shared
197// `read_enum_header` / `read_data_header` helpers.
198impl<'de, T: FromWXF<'de>> FromWXF<'de> for Option<T> {
199    fn from_wxf_with_tag<R: Reader<'de>>(
200        r: &mut WxfReader<R>,
201        tok: ExpressionEnum,
202    ) -> Result<Self, Error> {
203        let (_n, variant) = crate::strategy::read_enum_header(r, tok)?;
204        match variant.as_str() {
205            "None" => Ok(None),
206            "Some" => {
207                crate::strategy::read_data_header(r, 1)?;
208                Ok(Some(T::from_wxf(r)?))
209            },
210            other => Err(Error::UnexpectedSymbol {
211                expected: vec!["None", "Some"],
212                got: other.to_owned(),
213            }),
214        }
215    }
216}
217
218impl<'de, T: FromWXF<'de>, E: FromWXF<'de>> FromWXF<'de> for Result<T, E> {
219    fn from_wxf_with_tag<R: Reader<'de>>(
220        r: &mut WxfReader<R>,
221        tok: ExpressionEnum,
222    ) -> Result<Self, Error> {
223        let (_n, variant) = crate::strategy::read_enum_header(r, tok)?;
224        match variant.as_str() {
225            "Ok" => {
226                crate::strategy::read_data_header(r, 1)?;
227                Ok(Ok(T::from_wxf(r)?))
228            },
229            "Err" => {
230                crate::strategy::read_data_header(r, 1)?;
231                Ok(Err(E::from_wxf(r)?))
232            },
233            other => Err(Error::UnexpectedSymbol {
234                expected: vec!["Ok", "Err"],
235                got: other.to_owned(),
236            }),
237        }
238    }
239}
240
241impl<'de, K, V> FromWXF<'de> for HashMap<K, V>
242where
243    K: FromWXF<'de> + Eq + std::hash::Hash,
244    V: FromWXF<'de>,
245{
246    fn from_wxf_with_tag<R: Reader<'de>>(
247        r: &mut WxfReader<R>,
248        tok: ExpressionEnum,
249    ) -> Result<Self, Error> {
250        if tok != ExpressionEnum::Association {
251            return Err(Error::unexpected_token(&["Association"], tok));
252        }
253        let n = r.read_varint()?;
254        let mut out = HashMap::with_capacity(n as usize);
255        for _ in 0..n {
256            let _delayed = r.read_rule()?;
257            let k = K::from_wxf(r)?;
258            let v = V::from_wxf(r)?;
259            out.insert(k, v);
260        }
261        Ok(out)
262    }
263}
264
265impl<'de, K, V> FromWXF<'de> for BTreeMap<K, V>
266where
267    K: FromWXF<'de> + Ord,
268    V: FromWXF<'de>,
269{
270    fn from_wxf_with_tag<R: Reader<'de>>(
271        r: &mut WxfReader<R>,
272        tok: ExpressionEnum,
273    ) -> Result<Self, Error> {
274        if tok != ExpressionEnum::Association {
275            return Err(Error::unexpected_token(&["Association"], tok));
276        }
277        let n = r.read_varint()?;
278        let mut out = BTreeMap::new();
279        for _ in 0..n {
280            let _delayed = r.read_rule()?;
281            let k = K::from_wxf(r)?;
282            let v = V::from_wxf(r)?;
283            out.insert(k, v);
284        }
285        Ok(out)
286    }
287}
288
289//==============================================================================
290// Vec<T>
291//==============================================================================
292
293// Owned `Vec<u8>` (= `wolfram_expr::ByteArray`) is the borrowed `&[u8]` read + a copy.
294impl<'de> FromWXF<'de> for Vec<u8> {
295    fn from_wxf_with_tag<R: Reader<'de>>(
296        r: &mut WxfReader<R>,
297        tok: ExpressionEnum,
298    ) -> Result<Self, Error> {
299        <&[u8] as FromWXF<'de>>::from_wxf_with_tag(r, tok).map(<[u8]>::to_vec)
300    }
301}
302
303// `Vec<numeric>` for the 9 non-u8 numeric primitives, via the widening helpers.
304macro_rules! impl_vec_numeric_from_wxf {
305    ($($t:ty),+ $(,)?) => {
306        $(
307            impl<'de> FromWXF<'de> for Vec<$t> {
308                fn from_wxf_with_tag<R: Reader<'de>>(r: &mut WxfReader<R>, tok: ExpressionEnum) -> Result<Self, Error> {
309                    crate::numeric_in::read_vec_with_tag::<$t, R>(r, tok, "")
310                }
311            }
312        )+
313    };
314}
315impl_vec_numeric_from_wxf!(i8, i16, i32, i64, u16, u32, u64, f32, f64);
316
317// `Vec<T>` for derived structs/enums → `Function[List, …]`.
318impl<'de, T: FromWXF<'de> + crate::to_wxf::WxfStruct> FromWXF<'de> for Vec<T> {
319    fn from_wxf_with_tag<R: Reader<'de>>(
320        r: &mut WxfReader<R>,
321        tok: ExpressionEnum,
322    ) -> Result<Self, Error> {
323        if tok != ExpressionEnum::Function {
324            return Err(Error::unexpected_token(&["Function"], tok));
325        }
326        let n = r.read_varint()?;
327        r.skip()?; // discard head (any head accepted)
328        let mut items = Vec::with_capacity(n as usize);
329        for _ in 0..n {
330            items.push(T::from_wxf(r)?);
331        }
332        Ok(items)
333    }
334}