Skip to main content

wolfram_serialize/
constants.rs

1//! WXF wire-format constants and enums.
2//!
3//! Mirrors `wolframclient/serializers/wxfencoder/constants.py`.
4
5#![allow(missing_docs)]
6
7use std::convert::TryFrom;
8
9//---- Internal byte values (private — callers use the enums below) ----
10
11const WXF_VERSION: u8 = b'8';
12const WXF_HEADER_SEPARATOR: u8 = b':';
13const WXF_HEADER_COMPRESS: u8 = b'C';
14
15const WXF_FUNCTION: u8 = b'f';
16const WXF_SYMBOL: u8 = b's';
17const WXF_STRING: u8 = b'S';
18const WXF_BYTE_ARRAY: u8 = b'B';
19const WXF_INTEGER8: u8 = b'C';
20const WXF_INTEGER16: u8 = b'j';
21const WXF_INTEGER32: u8 = b'i';
22const WXF_INTEGER64: u8 = b'L';
23const WXF_REAL64: u8 = b'r';
24const WXF_BIG_INTEGER: u8 = b'I';
25const WXF_BIG_REAL: u8 = b'R';
26const WXF_PACKED_ARRAY: u8 = 0xC1;
27const WXF_NUMERIC_ARRAY: u8 = 0xC2;
28const WXF_ASSOCIATION: u8 = b'A';
29const WXF_RULE: u8 = b'-';
30const WXF_RULE_DELAYED: u8 = b':';
31
32const WXF_ARRAY_INTEGER8: u8 = 0x00;
33const WXF_ARRAY_INTEGER16: u8 = 0x01;
34const WXF_ARRAY_INTEGER32: u8 = 0x02;
35const WXF_ARRAY_INTEGER64: u8 = 0x03;
36const WXF_ARRAY_UNSIGNED_INTEGER8: u8 = 0x10;
37const WXF_ARRAY_UNSIGNED_INTEGER16: u8 = 0x11;
38const WXF_ARRAY_UNSIGNED_INTEGER32: u8 = 0x12;
39const WXF_ARRAY_UNSIGNED_INTEGER64: u8 = 0x13;
40const WXF_ARRAY_REAL32: u8 = 0x22;
41const WXF_ARRAY_REAL64: u8 = 0x23;
42const WXF_ARRAY_COMPLEX_REAL32: u8 = 0x33;
43const WXF_ARRAY_COMPLEX_REAL64: u8 = 0x34;
44
45//---- Shared lookup tables (ExpressionEnum + NumericArrayEnum/PackedArrayEnum) ----
46//
47// Expression token bytes (0x2D–0xC2) and array element-type bytes (0x00–0x34)
48// do not overlap, so a single function covers both without ambiguity.
49// HeaderEnum bytes overlap with some expression bytes and are excluded.
50
51fn token_to_size_in_bytes(byte: u8) -> usize {
52    match byte {
53        WXF_ARRAY_INTEGER8 | WXF_ARRAY_UNSIGNED_INTEGER8 => 1,
54        WXF_ARRAY_INTEGER16 | WXF_ARRAY_UNSIGNED_INTEGER16 => 2,
55        WXF_ARRAY_INTEGER32 | WXF_ARRAY_UNSIGNED_INTEGER32 | WXF_ARRAY_REAL32 => 4,
56        WXF_ARRAY_INTEGER64
57        | WXF_ARRAY_UNSIGNED_INTEGER64
58        | WXF_ARRAY_REAL64
59        | WXF_ARRAY_COMPLEX_REAL32 => 8,
60        WXF_ARRAY_COMPLEX_REAL64 => 16,
61        _ => panic!("token_to_size_in_bytes: unknown byte 0x{:02X}", byte),
62    }
63}
64
65fn token_to_name(byte: u8) -> &'static str {
66    match byte {
67        WXF_FUNCTION => "Function",
68        WXF_SYMBOL => "Symbol",
69        WXF_STRING => "String",
70        WXF_BYTE_ARRAY => "ByteArray",
71        WXF_INTEGER8 => "Integer8",
72        WXF_INTEGER16 => "Integer16",
73        WXF_INTEGER32 => "Integer32",
74        WXF_INTEGER64 => "Integer64",
75        WXF_REAL64 => "Real64",
76        WXF_BIG_INTEGER => "BigInteger",
77        WXF_BIG_REAL => "BigReal",
78        WXF_PACKED_ARRAY => "PackedArray",
79        WXF_NUMERIC_ARRAY => "NumericArray",
80        WXF_ASSOCIATION => "Association",
81        WXF_RULE => "Rule",
82        WXF_RULE_DELAYED => "RuleDelayed",
83        WXF_ARRAY_INTEGER8 => "Integer8",
84        WXF_ARRAY_INTEGER16 => "Integer16",
85        WXF_ARRAY_INTEGER32 => "Integer32",
86        WXF_ARRAY_INTEGER64 => "Integer64",
87        WXF_ARRAY_UNSIGNED_INTEGER8 => "UnsignedInteger8",
88        WXF_ARRAY_UNSIGNED_INTEGER16 => "UnsignedInteger16",
89        WXF_ARRAY_UNSIGNED_INTEGER32 => "UnsignedInteger32",
90        WXF_ARRAY_UNSIGNED_INTEGER64 => "UnsignedInteger64",
91        WXF_ARRAY_REAL32 => "Real32",
92        WXF_ARRAY_REAL64 => "Real64",
93        WXF_ARRAY_COMPLEX_REAL32 => "ComplexReal32",
94        WXF_ARRAY_COMPLEX_REAL64 => "ComplexReal64",
95        _ => "<unknown>",
96    }
97}
98
99//======================================
100// HeaderEnum
101//======================================
102
103/// WXF framing header bytes. No Display — header bytes overlap with some
104/// expression token bytes and are not used in error messages.
105#[derive(Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive)]
106#[repr(u8)]
107pub enum HeaderEnum {
108    Version = WXF_VERSION,
109    Separator = WXF_HEADER_SEPARATOR,
110    Compress = WXF_HEADER_COMPRESS,
111}
112
113//======================================
114// ExpressionEnum — top-level WXF token
115//======================================
116
117/// Top-level WXF expression token. `#[repr(u8)]` discriminants are the wire bytes.
118#[derive(Debug, Copy, Clone, PartialEq, Eq, num_enum::TryFromPrimitive)]
119#[repr(u8)]
120pub enum ExpressionEnum {
121    Function = WXF_FUNCTION,
122    Symbol = WXF_SYMBOL,
123    String = WXF_STRING,
124    ByteArray = WXF_BYTE_ARRAY,
125    Integer8 = WXF_INTEGER8,
126    Integer16 = WXF_INTEGER16,
127    Integer32 = WXF_INTEGER32,
128    Integer64 = WXF_INTEGER64,
129    Real64 = WXF_REAL64,
130    BigInteger = WXF_BIG_INTEGER,
131    BigReal = WXF_BIG_REAL,
132    PackedArray = WXF_PACKED_ARRAY,
133    NumericArray = WXF_NUMERIC_ARRAY,
134    Association = WXF_ASSOCIATION,
135    Rule = WXF_RULE,
136    RuleDelayed = WXF_RULE_DELAYED,
137}
138
139impl ExpressionEnum {
140    pub fn name(self) -> &'static str {
141        token_to_name(self as u8)
142    }
143}
144//======================================
145// NumericArrayEnum — element type
146//======================================
147
148/// WXF element-type tag for NumericArray. Discriminants are the WXF wire bytes.
149#[derive(
150    Debug,
151    Copy,
152    Clone,
153    PartialEq,
154    Eq,
155    PartialOrd,
156    Ord,
157    Hash,
158    num_enum::TryFromPrimitive
159)]
160#[repr(u8)]
161pub enum NumericArrayEnum {
162    Integer8 = WXF_ARRAY_INTEGER8,
163    Integer16 = WXF_ARRAY_INTEGER16,
164    Integer32 = WXF_ARRAY_INTEGER32,
165    Integer64 = WXF_ARRAY_INTEGER64,
166    UnsignedInteger8 = WXF_ARRAY_UNSIGNED_INTEGER8,
167    UnsignedInteger16 = WXF_ARRAY_UNSIGNED_INTEGER16,
168    UnsignedInteger32 = WXF_ARRAY_UNSIGNED_INTEGER32,
169    UnsignedInteger64 = WXF_ARRAY_UNSIGNED_INTEGER64,
170    Real32 = WXF_ARRAY_REAL32,
171    Real64 = WXF_ARRAY_REAL64,
172    ComplexReal32 = WXF_ARRAY_COMPLEX_REAL32,
173    ComplexReal64 = WXF_ARRAY_COMPLEX_REAL64,
174}
175
176impl NumericArrayEnum {
177    pub fn size_in_bytes(self) -> usize {
178        token_to_size_in_bytes(self as u8)
179    }
180
181    pub fn name(self) -> &'static str {
182        token_to_name(self as u8)
183    }
184}
185
186//======================================
187// PackedArrayEnum — element type (packed-compatible subset)
188//======================================
189
190/// WXF element-type tag for PackedArray. Same wire bytes as [`NumericArrayEnum`]
191/// but restricted to the packed-compatible variants (no unsigned integers).
192#[derive(
193    Debug,
194    Copy,
195    Clone,
196    PartialEq,
197    Eq,
198    PartialOrd,
199    Ord,
200    Hash,
201    num_enum::TryFromPrimitive
202)]
203#[repr(u8)]
204pub enum PackedArrayEnum {
205    Integer8 = WXF_ARRAY_INTEGER8,
206    Integer16 = WXF_ARRAY_INTEGER16,
207    Integer32 = WXF_ARRAY_INTEGER32,
208    Integer64 = WXF_ARRAY_INTEGER64,
209    Real32 = WXF_ARRAY_REAL32,
210    Real64 = WXF_ARRAY_REAL64,
211    ComplexReal32 = WXF_ARRAY_COMPLEX_REAL32,
212    ComplexReal64 = WXF_ARRAY_COMPLEX_REAL64,
213}
214
215impl PackedArrayEnum {
216    pub fn size_in_bytes(self) -> usize {
217        token_to_size_in_bytes(self as u8)
218    }
219
220    pub fn name(self) -> &'static str {
221        token_to_name(self as u8)
222    }
223}
224
225impl From<PackedArrayEnum> for NumericArrayEnum {
226    fn from(p: PackedArrayEnum) -> Self {
227        NumericArrayEnum::try_from(p as u8)
228            .expect("PackedArrayEnum byte is always valid NumericArrayEnum")
229    }
230}
231
232impl TryFrom<NumericArrayEnum> for PackedArrayEnum {
233    type Error = ();
234    fn try_from(n: NumericArrayEnum) -> Result<Self, ()> {
235        PackedArrayEnum::try_from(n as u8).map_err(|_| ())
236    }
237}
238
239#[cfg(test)]
240mod tests {
241    use super::*;
242    use std::convert::TryFrom;
243
244    #[test]
245    fn expression_enum_try_from_known() {
246        assert_eq!(
247            ExpressionEnum::try_from(WXF_ASSOCIATION),
248            Ok(ExpressionEnum::Association)
249        );
250    }
251
252    #[test]
253    fn expression_enum_try_from_invalid() {
254        assert!(ExpressionEnum::try_from(0xFF_u8).is_err());
255    }
256
257    #[test]
258    fn numeric_array_enum_try_from_known() {
259        assert_eq!(
260            NumericArrayEnum::try_from(WXF_ARRAY_INTEGER32),
261            Ok(NumericArrayEnum::Integer32)
262        );
263    }
264
265    #[test]
266    fn numeric_array_enum_try_from_invalid() {
267        assert!(NumericArrayEnum::try_from(0xFF_u8).is_err());
268    }
269
270    #[test]
271    fn packed_array_enum_rejects_unsigned() {
272        assert!(PackedArrayEnum::try_from(WXF_ARRAY_UNSIGNED_INTEGER8).is_err());
273    }
274
275    #[test]
276    fn packed_to_numeric_roundtrip() {
277        let p = PackedArrayEnum::Integer32;
278        let n = NumericArrayEnum::from(p);
279        assert_eq!(n, NumericArrayEnum::Integer32);
280    }
281}