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