Skip to main content

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}