Skip to main content

wolfram_serialize/
to_wxf.rs

1//! [`ToWXF`] — per-Rust-type WXF encoder. Streams directly into a
2//! [`WxfWriter`]: compounds write a header then recurse into children. No
3//! intermediate `Vec`, no `&dyn` dispatch — fully monomorphized.
4
5use crate::constants::NumericArrayEnum;
6use crate::writer::Writer;
7use crate::wxf::writer::WxfWriter;
8use crate::Error;
9
10/// Types that know how to serialize themselves into a WXF stream.
11pub trait ToWXF {
12    /// Write `self` to `w` as a complete WXF value (tag + payload).
13    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error>;
14}
15
16/// Marker auto-implemented by `#[derive(ToWXF)]` for user structs/enums. Gates
17/// the blanket `impl ToWXF for Vec<T>` (List form) so it can't conflict with the
18/// numeric-primitive `Vec` specializations.
19pub trait WxfStruct {}
20
21//==============================================================================
22// Primitive impls
23//==============================================================================
24
25macro_rules! impl_to_wxf_int {
26    ($($t:ty),+) => {
27        $(
28            impl ToWXF for $t {
29                fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
30                    w.write_integer(i64::from(*self))
31                }
32            }
33        )+
34    };
35}
36impl_to_wxf_int!(i8, i16, i32, i64, u8, u16, u32);
37
38impl ToWXF for u64 {
39    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
40        // u64 may exceed i64::MAX; clamp (full range needs BigInteger).
41        w.write_integer(i64::try_from(*self).unwrap_or(i64::MAX))
42    }
43}
44
45impl ToWXF for f32 {
46    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
47        w.write_real(f64::from(*self))
48    }
49}
50
51impl ToWXF for f64 {
52    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
53        w.write_real(*self)
54    }
55}
56
57impl ToWXF for bool {
58    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
59        w.write_symbol(if *self { "System`True" } else { "System`False" })
60    }
61}
62
63impl ToWXF for str {
64    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
65        w.write_string(self)
66    }
67}
68
69impl ToWXF for String {
70    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
71        w.write_string(self.as_str())
72    }
73}
74
75impl<T: ToWXF + ?Sized> ToWXF for &T {
76    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
77        (*self).to_wxf(w)
78    }
79}
80
81impl<T: ToWXF + ?Sized> ToWXF for Box<T> {
82    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
83        (**self).to_wxf(w)
84    }
85}
86
87//==============================================================================
88// Vec / slice impls
89//==============================================================================
90
91// `Vec<u8>` and `[u8]` → ByteArray.
92impl ToWXF for [u8] {
93    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
94        w.write_byte_array(self)
95    }
96}
97impl ToWXF for Vec<u8> {
98    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
99        w.write_byte_array(self)
100    }
101}
102
103// `[T]` / `Vec<T>` for the 9 numeric primitives that aren't `u8` → 1-D
104// NumericArray. Zero-copy: bytes flow straight from the slice's storage.
105macro_rules! impl_slice_numeric {
106    ($($t:ty => $variant:ident),+ $(,)?) => {
107        $(
108            impl ToWXF for [$t] {
109                fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
110                    // SAFETY: `$t` is a numeric primitive; the bytes of `&[$t]`
111                    // are a valid little-endian flat buffer for that element type.
112                    let bytes: &[u8] = unsafe {
113                        ::core::slice::from_raw_parts(
114                            self.as_ptr() as *const u8,
115                            ::core::mem::size_of::<$t>() * self.len(),
116                        )
117                    };
118                    w.write_numeric_array(NumericArrayEnum::$variant, &[self.len()], bytes)
119                }
120            }
121
122            impl ToWXF for Vec<$t> {
123                fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
124                    self.as_slice().to_wxf(w)
125                }
126            }
127        )+
128    };
129}
130impl_slice_numeric!(
131    i8  => Integer8,
132    i16 => Integer16,
133    i32 => Integer32,
134    i64 => Integer64,
135    u16 => UnsignedInteger16,
136    u32 => UnsignedInteger32,
137    u64 => UnsignedInteger64,
138    f32 => Real32,
139    f64 => Real64,
140);
141
142// `Vec<T>` for derived structs/enums → `Function[List, …]`.
143impl<T: ToWXF + WxfStruct> ToWXF for Vec<T> {
144    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
145        w.write_function(self.len())?;
146        w.write_symbol("System`List")?;
147        for e in self {
148            e.to_wxf(w)?;
149        }
150        Ok(())
151    }
152}
153
154//==============================================================================
155// Option / unit / maps
156//==============================================================================
157
158impl ToWXF for () {
159    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
160        w.write_symbol("System`Null")
161    }
162}
163
164// Option<T> and Result<T, E> are ordinary enums on the wire — identical to what
165// `#[derive(ToWXF)]` produces for a user enum of the same shape:
166//   None    → <|"Enum" -> "None"|>
167//   Some(v) → <|"Enum" -> "Some", "Data" -> {v}|>
168//   Ok(v)   → <|"Enum" -> "Ok",   "Data" -> {v}|>
169//   Err(e)  → <|"Enum" -> "Err",  "Data" -> {e}|>
170impl<T: ToWXF> ToWXF for Option<T> {
171    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
172        match self {
173            None => crate::strategy::write_unit_variant(
174                w,
175                crate::strategy::DEFAULT_ENUM_HEAD,
176                "None",
177            ),
178            Some(v) => {
179                crate::strategy::begin_data_variant(
180                    w,
181                    crate::strategy::DEFAULT_ENUM_HEAD,
182                    "Some",
183                    1,
184                )?;
185                v.to_wxf(w)
186            },
187        }
188    }
189}
190
191impl<T: ToWXF, E: ToWXF> ToWXF for Result<T, E> {
192    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
193        match self {
194            Ok(v) => {
195                crate::strategy::begin_data_variant(
196                    w,
197                    crate::strategy::DEFAULT_ENUM_HEAD,
198                    "Ok",
199                    1,
200                )?;
201                v.to_wxf(w)
202            },
203            Err(e) => {
204                crate::strategy::begin_data_variant(
205                    w,
206                    crate::strategy::DEFAULT_ENUM_HEAD,
207                    "Err",
208                    1,
209                )?;
210                e.to_wxf(w)
211            },
212        }
213    }
214}
215
216impl<K: ToWXF, V: ToWXF, S> ToWXF for std::collections::HashMap<K, V, S> {
217    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
218        w.write_association(self.len())?;
219        for (k, v) in self {
220            w.write_rule(false)?;
221            k.to_wxf(w)?;
222            v.to_wxf(w)?;
223        }
224        Ok(())
225    }
226}
227
228impl<K: ToWXF, V: ToWXF> ToWXF for std::collections::BTreeMap<K, V> {
229    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
230        w.write_association(self.len())?;
231        for (k, v) in self {
232            w.write_rule(false)?;
233            k.to_wxf(w)?;
234            v.to_wxf(w)?;
235        }
236        Ok(())
237    }
238}
239
240// ToWXF impls for the `wolfram-expr` value types (Expr, Symbol, Association,
241// NumericArray, PackedArray, BigInteger, BigReal) live in `wolfram-expr`, which
242// depends on this crate.