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}