Skip to main content

wolfram_serialize/wxf/
writer.rs

1//! Typed, streaming WXF writer — sugar over a raw [`Writer`].
2//!
3//! Atoms write their tag + payload; compounds write only a header
4//! ([`write_function`][WxfWriter::write_function] /
5//! [`write_association`][WxfWriter::write_association]) and the caller streams
6//! the children next. No intermediate buffering of the structure, no `dyn`.
7
8use crate::constants::{ExpressionEnum, NumericArrayEnum, PackedArrayEnum};
9use crate::writer::Writer;
10use crate::Error;
11
12/// Typed WXF writer wrapping a raw byte [`Writer`].
13pub struct WxfWriter<W> {
14    inner: W,
15}
16
17impl<W: Writer> WxfWriter<W> {
18    /// Wrap a raw writer. The writer emits only the WXF token stream — the
19    /// `8:` / `8C:` header is framing, written by [`crate::to_wxf`][fn@crate::to_wxf].
20    pub fn new(inner: W) -> Self {
21        WxfWriter { inner }
22    }
23
24    /// Consume the writer, returning the underlying sink.
25    pub fn into_inner(self) -> W {
26        self.inner
27    }
28
29    //---- raw / framing --------------------------------------------------
30
31    /// Write a WXF varint (LEB128, 7-bit groups, little-endian).
32    pub fn write_varint(&mut self, n: u64) -> Result<(), Error> {
33        let mut value = n;
34        loop {
35            let mut byte = (value & 0x7F) as u8;
36            value >>= 7;
37            if value != 0 {
38                byte |= 0x80;
39                self.inner.write_byte(byte)?;
40            } else {
41                self.inner.write_byte(byte)?;
42                return Ok(());
43            }
44        }
45    }
46
47    /// Write a single expression token byte.
48    pub fn write_expr_token(&mut self, t: ExpressionEnum) -> Result<(), Error> {
49        self.inner.write_byte(t as u8)
50    }
51
52    //---- atoms (tag + payload) ------------------------------------------
53
54    /// Write an integer using the smallest of Integer8/16/32/64.
55    pub fn write_integer(&mut self, n: i64) -> Result<(), Error> {
56        if let Ok(v) = i8::try_from(n) {
57            self.write_expr_token(ExpressionEnum::Integer8)?;
58            self.inner.write_bytes(&v.to_le_bytes())
59        } else if let Ok(v) = i16::try_from(n) {
60            self.write_expr_token(ExpressionEnum::Integer16)?;
61            self.inner.write_bytes(&v.to_le_bytes())
62        } else if let Ok(v) = i32::try_from(n) {
63            self.write_expr_token(ExpressionEnum::Integer32)?;
64            self.inner.write_bytes(&v.to_le_bytes())
65        } else {
66            self.write_expr_token(ExpressionEnum::Integer64)?;
67            self.inner.write_bytes(&n.to_le_bytes())
68        }
69    }
70
71    /// Write a `Real64`.
72    pub fn write_real(&mut self, f: f64) -> Result<(), Error> {
73        self.write_expr_token(ExpressionEnum::Real64)?;
74        self.inner.write_bytes(&f.to_le_bytes())
75    }
76
77    /// Write a `String`.
78    pub fn write_string(&mut self, s: &str) -> Result<(), Error> {
79        self.write_length_prefixed(ExpressionEnum::String, s.as_bytes())
80    }
81
82    /// Write a `Symbol` (fully-qualified name).
83    pub fn write_symbol(&mut self, name: &str) -> Result<(), Error> {
84        self.write_length_prefixed(ExpressionEnum::Symbol, name.as_bytes())
85    }
86
87    /// Write a `ByteArray`.
88    pub fn write_byte_array(&mut self, bytes: &[u8]) -> Result<(), Error> {
89        self.write_length_prefixed(ExpressionEnum::ByteArray, bytes)
90    }
91
92    /// Write a `BigInteger` from its decimal digit string.
93    pub fn write_big_integer(&mut self, digits: &str) -> Result<(), Error> {
94        self.write_length_prefixed(ExpressionEnum::BigInteger, digits.as_bytes())
95    }
96
97    /// Write a `BigReal` from its digit string.
98    pub fn write_big_real(&mut self, digits: &str) -> Result<(), Error> {
99        self.write_length_prefixed(ExpressionEnum::BigReal, digits.as_bytes())
100    }
101
102    /// Write a `NumericArray` from raw parts.
103    pub fn write_numeric_array(
104        &mut self,
105        dt: NumericArrayEnum,
106        dims: &[usize],
107        bytes: &[u8],
108    ) -> Result<(), Error> {
109        self.write_expr_token(ExpressionEnum::NumericArray)?;
110        self.inner.write_byte(dt as u8)?;
111        self.write_dims(dims)?;
112        self.inner.write_bytes(bytes)
113    }
114
115    /// Write a `PackedArray` from raw parts.
116    pub fn write_packed_array(
117        &mut self,
118        dt: PackedArrayEnum,
119        dims: &[usize],
120        bytes: &[u8],
121    ) -> Result<(), Error> {
122        self.write_expr_token(ExpressionEnum::PackedArray)?;
123        self.inner.write_byte(NumericArrayEnum::from(dt) as u8)?;
124        self.write_dims(dims)?;
125        self.inner.write_bytes(bytes)
126    }
127
128    //---- compounds (header only; caller streams children) ---------------
129
130    /// Write a `Function` header (`head[args…]`): the token + arity. The caller
131    /// next writes the head value, then `arity` argument values.
132    pub fn write_function(&mut self, arity: usize) -> Result<(), Error> {
133        self.write_expr_token(ExpressionEnum::Function)?;
134        self.write_varint(arity as u64)
135    }
136
137    /// Write an `Association` header: the token + entry count. The caller next
138    /// writes `count` × (`write_rule`, key, value).
139    pub fn write_association(&mut self, count: usize) -> Result<(), Error> {
140        self.write_expr_token(ExpressionEnum::Association)?;
141        self.write_varint(count as u64)
142    }
143
144    /// Write a `Rule` (or `RuleDelayed`) token.
145    pub fn write_rule(&mut self, delayed: bool) -> Result<(), Error> {
146        self.write_expr_token(if delayed {
147            ExpressionEnum::RuleDelayed
148        } else {
149            ExpressionEnum::Rule
150        })
151    }
152
153    //---- internal -------------------------------------------------------
154
155    fn write_length_prefixed(
156        &mut self,
157        token: ExpressionEnum,
158        bytes: &[u8],
159    ) -> Result<(), Error> {
160        self.write_expr_token(token)?;
161        self.write_varint(bytes.len() as u64)?;
162        self.inner.write_bytes(bytes)
163    }
164
165    fn write_dims(&mut self, dims: &[usize]) -> Result<(), Error> {
166        self.write_varint(dims.len() as u64)?;
167        for d in dims {
168            self.write_varint(*d as u64)?;
169        }
170        Ok(())
171    }
172}