Skip to main content

wolfram_serialize/
strategy.rs

1//! Encoding *strategies* — the conventions for mapping Rust shapes onto WXF
2//! expressions. These sit a layer above the cursor ([`WxfReader`]/[`WxfWriter`],
3//! which only know raw WXF tokens) and are shared by the `#[derive]` codegen and
4//! the hand-written std impls (`Option`, `Result`, …) so the wire format lives
5//! in exactly one place.
6//!
7//! ## Enum representation
8//!
9//! A Rust enum is encoded as a `List` where the first element is the variant
10//! name (a string) and the remaining elements are the payload:
11//!
12//! ```text
13//! None            {"None"}
14//! Some(v)         {"Some", v}
15//! Rect(w, h)      {"Rect", w, h}
16//! ```
17
18use crate::constants::ExpressionEnum;
19use crate::reader::Reader;
20use crate::writer::Writer;
21use crate::wxf::reader::WxfReader;
22use crate::wxf::writer::WxfWriter;
23use crate::Error;
24
25//---- write ------------------------------------------------------------------
26
27/// Default head for enum variants on the wire: `{"VariantName", data...}`.
28pub const DEFAULT_ENUM_HEAD: &str = "System`List";
29
30/// Write a unit variant: `head["VariantName"]`.
31/// Use [`DEFAULT_ENUM_HEAD`] for the standard `{"VariantName"}` form.
32pub fn write_unit_variant<W: Writer>(
33    w: &mut WxfWriter<W>,
34    head: &str,
35    name: &str,
36) -> Result<(), Error> {
37    w.write_function(1)?;
38    w.write_symbol(head)?;
39    w.write_string(name)
40}
41
42/// Begin a data-carrying variant: `head["VariantName", data...]`.
43/// The caller writes the `n_data` payload values immediately after.
44pub fn begin_data_variant<W: Writer>(
45    w: &mut WxfWriter<W>,
46    head: &str,
47    name: &str,
48    n_data: usize,
49) -> Result<(), Error> {
50    w.write_function(1 + n_data)?;
51    w.write_symbol(head)?;
52    w.write_string(name)
53}
54
55//---- read -------------------------------------------------------------------
56
57/// Read an enum list header (token already consumed): skips the head, reads the
58/// variant name string, and returns `(total_arity, variant_name)`. The caller
59/// reads the remaining `total_arity - 1` payload values.
60pub fn read_enum_header<'de, R: Reader<'de>>(
61    r: &mut WxfReader<R>,
62    tok: ExpressionEnum,
63) -> Result<(u64, String), Error> {
64    match tok {
65        // Full form: {"VariantName", data...}
66        ExpressionEnum::Function => {
67            let n = r.read_varint()?;
68            if n == 0 {
69                return Err(Error::invalid("enum List is empty".into()));
70            }
71            r.skip()?; // discard head
72            let variant = r.read_string()?;
73            Ok((n, variant))
74        },
75        // Shorthand: "VariantName" — unit variant with no data.
76        ExpressionEnum::String => {
77            let variant = r.read_str()?.to_owned();
78            Ok((1, variant))
79        },
80        other => Err(Error::unexpected_token(&["Function", "String"], other)),
81    }
82}
83
84/// No-op: the old strategy needed a separate `"Data"` key + List header;
85/// the new format inlines data directly after the variant name, so there
86/// is nothing extra to read. Kept for API compatibility with derived code.
87pub fn read_data_header<'de, R: Reader<'de>>(
88    _r: &mut WxfReader<R>,
89    _n_data: usize,
90) -> Result<(), Error> {
91    Ok(())
92}