wolfram_serialize/errors.rs
1//! The crate-wide WXF error type.
2//!
3//! [`Error`] is the error type of [`ToWXF`][crate::ToWXF] / [`FromWXF`][crate::FromWXF],
4//! so *every* serialization failure across the whole workspace funnels through it.
5//! It carries only `Debug` — no `Display`/[`std::error::Error`] impls — and does **not**
6//! serialize itself to a Wolfram `Failure[…]`. When a caller needs to surface a failure to
7//! the kernel, it builds one explicitly with `expr!` (e.g. the WXF export bridge wraps an
8//! arg-decode `Error` as `Failure["ArgumentError", <|"Message" -> …|>]`).
9//!
10//! Design: structured data is carried only where it's *useful* — an unexpected token
11//! reports the tokens it would have accepted and the one it got, a size mismatch reports
12//! both counts, a typed field error reports the path. Everything else — malformed headers,
13//! truncated varints, unknown bytes, bad UTF-8 — is a plain [`Invalid`][Error::Invalid]
14//! with a `message`. The `Debug` text carries this detail.
15
16use crate::constants::ExpressionEnum;
17
18/// Errors returned by [`to_wxf`][crate::to_wxf] / [`from_wxf`][crate::from_wxf] and every
19/// [`ToWXF`][crate::ToWXF] / [`FromWXF`][crate::FromWXF] impl.
20#[derive(Debug)]
21pub enum Error {
22 /// An underlying [`std::io::Error`] from a writer, flattened to its message.
23 Io {
24 /// `Display` text of the underlying I/O error.
25 message: String,
26 },
27 /// Malformed input with no further structured data worth surfacing: bad/short
28 /// header, wrong version/separator, truncated or over-long varint, unexpected
29 /// EOF, byte-count overflow, unknown token/element byte, invalid UTF-8, an
30 /// empty enum, a `NaN` real, an unparseable symbol name, or an unsupported
31 /// packed-array element type. The `message` says which.
32 Invalid {
33 /// Human-readable description of what was malformed.
34 message: String,
35 },
36 /// Got a token that isn't one of the accepted set for this position. An empty
37 /// `expected` means "any value token except the one we got" (e.g. a `Rule`
38 /// where a value was required).
39 UnexpectedToken {
40 /// Token names that would have been accepted.
41 expected: Vec<&'static str>,
42 /// The token name actually read.
43 got: &'static str,
44 },
45 /// Got a `Symbol` whose name isn't one of the accepted set (e.g. `True`/`False`,
46 /// or an `Option`/`Result` variant name).
47 UnexpectedSymbol {
48 /// Symbol / variant names that would have been accepted.
49 expected: Vec<&'static str>,
50 /// The name actually read.
51 got: String,
52 },
53 /// A `Function`'s argument count didn't match what the caller expected.
54 ArgCountMismatch {
55 /// Arguments the caller expected.
56 expected: u64,
57 /// Arguments actually present.
58 got: u64,
59 },
60 /// Type mismatch during typed deserialization via [`FromWXF`][crate::FromWXF].
61 /// Threaded with a dotted field `path` by the derive (e.g. `"Frame.payload"`).
62 Deserialize {
63 /// Dotted field accessor, e.g. `"Frame.payload"`.
64 path: String,
65 /// Description of the expected wire shape.
66 expected: &'static str,
67 /// Description of the actual wire shape encountered.
68 got: String,
69 },
70}
71
72impl Error {
73 /// Build an [`Error::Invalid`] from a message — for malformed input that has
74 /// no further structured data worth a dedicated variant.
75 pub fn invalid(message: String) -> Self {
76 Error::Invalid { message }
77 }
78
79 /// Build an [`Error::UnexpectedToken`] from the accepted token names and the
80 /// token actually read. An empty `expected` slice means "any token but this".
81 pub fn unexpected_token(expected: &[&'static str], got: ExpressionEnum) -> Self {
82 Error::UnexpectedToken {
83 expected: expected.to_vec(),
84 got: got.name(),
85 }
86 }
87}
88
89impl From<std::io::Error> for Error {
90 fn from(e: std::io::Error) -> Self {
91 Error::Io {
92 message: e.to_string(),
93 }
94 }
95}