facet_postcard/
error.rs

1use alloc::string::String;
2use core::fmt;
3
4use facet_core::{ScalarType, Shape};
5use facet_path::Path;
6use facet_reflect::ReflectError;
7
8#[cfg(feature = "pretty-errors")]
9use facet_path::pretty::PathDiagnostic;
10
11/// Errors that can occur during postcard serialization
12#[derive(Debug)]
13pub enum SerializeError {
14    /// The output buffer is too small to hold the serialized data
15    BufferTooSmall,
16    /// Encountered a type that cannot be serialized to postcard format
17    UnsupportedType(&'static str),
18    /// Encountered a scalar type that postcard doesn't support
19    UnsupportedScalar {
20        /// The scalar type that was encountered
21        scalar_type: ScalarType,
22        /// The path in the data structure where this occurred
23        path: Path,
24        /// The root shape for formatting the path
25        root_shape: &'static Shape,
26    },
27    /// Encountered an unknown scalar (scalar_type() returned None)
28    UnknownScalar {
29        /// The type name from the shape
30        type_name: &'static str,
31        /// The path in the data structure where this occurred
32        path: Path,
33        /// The root shape for formatting the path
34        root_shape: &'static Shape,
35    },
36}
37
38// Re-export for convenience
39pub use facet_path::{Path as ErrorPath, PathStep as ErrorPathStep};
40
41impl SerializeError {
42    /// Format error as a simple one-line message (without pretty rendering)
43    pub fn format_simple(&self) -> String {
44        match self {
45            SerializeError::BufferTooSmall => String::from("Buffer too small for serialized data"),
46            SerializeError::UnsupportedType(ty) => {
47                alloc::format!("Unsupported type for postcard serialization: {ty}")
48            }
49            SerializeError::UnsupportedScalar {
50                scalar_type,
51                path,
52                root_shape,
53            } => {
54                let path_str = path.format_with_shape(root_shape);
55                alloc::format!(
56                    "Unsupported scalar type {:?} at path: {}",
57                    scalar_type,
58                    path_str
59                )
60            }
61            SerializeError::UnknownScalar {
62                type_name,
63                path,
64                root_shape,
65            } => {
66                let path_str = path.format_with_shape(root_shape);
67                alloc::format!("Unknown scalar type '{}' at path: {}", type_name, path_str)
68            }
69        }
70    }
71
72    /// Format error with pretty rendering (requires `pretty-errors` feature)
73    #[cfg(feature = "pretty-errors")]
74    pub fn format_pretty(&self) -> String {
75        match self {
76            SerializeError::BufferTooSmall => String::from("Buffer too small for serialized data"),
77            SerializeError::UnsupportedType(ty) => {
78                alloc::format!("Unsupported type for postcard serialization: {ty}")
79            }
80            SerializeError::UnsupportedScalar {
81                scalar_type,
82                path,
83                root_shape,
84            } => {
85                let message = alloc::format!("Unsupported scalar type: {:?}", scalar_type);
86                path.format_pretty(root_shape, message, None)
87            }
88            SerializeError::UnknownScalar {
89                type_name,
90                path,
91                root_shape,
92            } => {
93                let message = alloc::format!("Unknown scalar type: {}", type_name);
94                path.format_pretty(root_shape, message, None)
95            }
96        }
97    }
98
99    /// Format error with pretty rendering but without colors (for testing)
100    #[cfg(feature = "pretty-errors")]
101    pub fn format_pretty_no_color(&self) -> String {
102        match self {
103            SerializeError::BufferTooSmall => String::from("Buffer too small for serialized data"),
104            SerializeError::UnsupportedType(ty) => {
105                alloc::format!("Unsupported type for postcard serialization: {ty}")
106            }
107            SerializeError::UnsupportedScalar {
108                scalar_type,
109                path,
110                root_shape,
111            } => {
112                let message = alloc::format!("Unsupported scalar type: {:?}", scalar_type);
113                path.format_pretty_no_color(root_shape, message, None)
114            }
115            SerializeError::UnknownScalar {
116                type_name,
117                path,
118                root_shape,
119            } => {
120                let message = alloc::format!("Unknown scalar type: {}", type_name);
121                path.format_pretty_no_color(root_shape, message, None)
122            }
123        }
124    }
125}
126
127impl fmt::Display for SerializeError {
128    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129        #[cfg(feature = "pretty-errors")]
130        {
131            write!(f, "{}", self.format_pretty())
132        }
133        #[cfg(not(feature = "pretty-errors"))]
134        {
135            write!(f, "{}", self.format_simple())
136        }
137    }
138}
139
140#[cfg(feature = "std")]
141impl std::error::Error for SerializeError {}
142
143#[cfg(feature = "pretty-errors")]
144impl miette::Diagnostic for SerializeError {
145    fn source_code(&self) -> Option<&dyn miette::SourceCode> {
146        // We delegate to PathDiagnostic for the actual rendering via to_diagnostic()
147        None
148    }
149
150    fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn miette::Diagnostic> + 'a>> {
151        // PathDiagnostic handles related diagnostics internally
152        None
153    }
154
155    fn diagnostic_source(&self) -> Option<&dyn miette::Diagnostic> {
156        None
157    }
158}
159
160impl SerializeError {
161    /// Convert to a PathDiagnostic for rich error display
162    #[cfg(feature = "pretty-errors")]
163    pub fn to_diagnostic(&self) -> Option<PathDiagnostic> {
164        match self {
165            SerializeError::UnsupportedScalar {
166                scalar_type,
167                path,
168                root_shape,
169            } => {
170                let message = alloc::format!("Unsupported scalar type: {:?}", scalar_type);
171                Some(path.to_diagnostic(root_shape, message, None))
172            }
173            SerializeError::UnknownScalar {
174                type_name,
175                path,
176                root_shape,
177            } => {
178                let message = alloc::format!("Unsupported scalar type: {}", type_name);
179                Some(path.to_diagnostic(root_shape, message, None))
180            }
181            _ => None,
182        }
183    }
184}
185
186/// Errors that can occur during postcard deserialization
187#[derive(Debug)]
188pub enum DeserializeError {
189    /// Not enough data available to decode a complete value
190    UnexpectedEnd,
191    /// The data is malformed or corrupted
192    InvalidData,
193    /// Integer value is too large for the target type
194    IntegerOverflow,
195    /// Encountered a field name that isn't recognized
196    UnknownField,
197    /// Required field is missing from the input
198    MissingField(&'static str),
199    /// Shape is not supported for deserialization
200    UnsupportedShape,
201    /// Type is not supported for deserialization
202    UnsupportedType(&'static str),
203    /// Invalid enum variant index
204    InvalidVariant,
205    /// Invalid boolean value (not 0 or 1)
206    InvalidBool,
207    /// Invalid UTF-8 in string
208    InvalidUtf8,
209    /// Reflection error from facet-reflect
210    ReflectError(ReflectError),
211    /// Sequence length mismatch
212    LengthMismatch,
213}
214
215impl From<ReflectError> for DeserializeError {
216    fn from(err: ReflectError) -> Self {
217        Self::ReflectError(err)
218    }
219}
220
221impl fmt::Display for DeserializeError {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        match self {
224            DeserializeError::UnexpectedEnd => write!(f, "Unexpected end of input"),
225            DeserializeError::InvalidData => write!(f, "Invalid postcard data"),
226            DeserializeError::IntegerOverflow => {
227                write!(f, "Integer value too large for target type")
228            }
229            DeserializeError::UnknownField => write!(f, "Unknown field encountered"),
230            DeserializeError::MissingField(field) => {
231                write!(f, "Missing required field: {field}")
232            }
233            DeserializeError::UnsupportedShape => {
234                write!(f, "Unsupported shape for deserialization")
235            }
236            DeserializeError::UnsupportedType(ty) => {
237                write!(f, "Unsupported type for deserialization: {ty}")
238            }
239            DeserializeError::InvalidVariant => write!(f, "Invalid enum variant index"),
240            DeserializeError::InvalidBool => write!(f, "Invalid boolean value (expected 0 or 1)"),
241            DeserializeError::InvalidUtf8 => write!(f, "Invalid UTF-8 in string data"),
242            DeserializeError::ReflectError(err) => write!(f, "Reflection error: {err}"),
243            DeserializeError::LengthMismatch => write!(f, "Sequence length mismatch"),
244        }
245    }
246}
247
248#[cfg(feature = "std")]
249impl std::error::Error for DeserializeError {}