1extern crate alloc;
4
5use alloc::string::String;
6use alloc::vec::Vec;
7use core::fmt;
8
9#[derive(Debug, Clone)]
11pub struct PostcardError {
12 pub code: i32,
14 pub pos: usize,
16 pub message: String,
18 pub source_bytes: Option<Vec<u8>>,
20}
21
22impl fmt::Display for PostcardError {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 write!(f, "{} at position {}", self.message, self.pos)?;
25 if let Some(ref bytes) = self.source_bytes {
26 let context = self.hex_context(bytes);
28 if !context.is_empty() {
29 write!(f, "\n{}", context)?;
30 }
31 }
32 Ok(())
33 }
34}
35
36impl PostcardError {
37 fn hex_context(&self, bytes: &[u8]) -> String {
39 use alloc::format;
40
41 if bytes.is_empty() {
42 return String::new();
43 }
44
45 let start = self.pos.saturating_sub(8);
47 let end = (self.pos + 8).min(bytes.len());
48
49 let mut parts = Vec::new();
50 for (i, byte) in bytes[start..end].iter().enumerate() {
51 let abs_pos = start + i;
52 if abs_pos == self.pos {
53 parts.push(format!("[{:02x}]", byte));
54 } else {
55 parts.push(format!("{:02x}", byte));
56 }
57 }
58
59 format!(
60 " bytes: {} (position {} marked with [])",
61 parts.join(" "),
62 self.pos
63 )
64 }
65
66 pub fn with_source(mut self, bytes: &[u8]) -> Self {
68 self.source_bytes = Some(bytes.to_vec());
69 self
70 }
71}
72
73impl std::error::Error for PostcardError {}
74
75#[cfg(feature = "pretty-errors")]
76impl miette::Diagnostic for PostcardError {
77 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
78 let code_name = match self.code {
79 codes::UNEXPECTED_EOF => "postcard::unexpected_eof",
80 codes::INVALID_BOOL => "postcard::invalid_bool",
81 codes::VARINT_OVERFLOW => "postcard::varint_overflow",
82 codes::SEQ_UNDERFLOW => "postcard::seq_underflow",
83 codes::INVALID_UTF8 => "postcard::invalid_utf8",
84 codes::INVALID_OPTION_DISCRIMINANT => "postcard::invalid_option",
85 codes::INVALID_ENUM_DISCRIMINANT => "postcard::invalid_enum",
86 codes::UNSUPPORTED_OPAQUE_TYPE => "postcard::unsupported_opaque",
87 codes::UNEXPECTED_END_OF_INPUT => "postcard::eof",
88 codes::UNSUPPORTED => "postcard::unsupported",
89 _ => "postcard::unknown",
90 };
91 Some(Box::new(code_name))
92 }
93
94 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
95 let help = match self.code {
96 codes::UNEXPECTED_EOF | codes::UNEXPECTED_END_OF_INPUT => {
97 "The input data is truncated or incomplete"
98 }
99 codes::INVALID_BOOL => "Boolean values must be 0x00 (false) or 0x01 (true)",
100 codes::VARINT_OVERFLOW => "Varint encoding used too many continuation bytes",
101 codes::INVALID_UTF8 => "String data contains invalid UTF-8 bytes",
102 codes::INVALID_OPTION_DISCRIMINANT => {
103 "Option discriminant must be 0x00 (None) or 0x01 (Some)"
104 }
105 codes::INVALID_ENUM_DISCRIMINANT => "Enum variant index is out of range for this type",
106 _ => return None,
107 };
108 Some(Box::new(help))
109 }
110}
111
112pub mod codes {
114 pub const UNEXPECTED_EOF: i32 = -100;
116 pub const INVALID_BOOL: i32 = -101;
118 pub const VARINT_OVERFLOW: i32 = -102;
120 pub const SEQ_UNDERFLOW: i32 = -103;
122 pub const INVALID_UTF8: i32 = -104;
124 pub const INVALID_OPTION_DISCRIMINANT: i32 = -105;
126 pub const INVALID_ENUM_DISCRIMINANT: i32 = -106;
128 pub const UNSUPPORTED_OPAQUE_TYPE: i32 = -107;
130 pub const UNEXPECTED_END_OF_INPUT: i32 = -108;
132 pub const UNSUPPORTED: i32 = -1;
134}
135
136impl PostcardError {
137 pub fn from_code(code: i32, pos: usize) -> Self {
139 let message = match code {
140 codes::UNEXPECTED_EOF => "unexpected end of input".to_string(),
141 codes::INVALID_BOOL => "invalid boolean value (expected 0 or 1)".to_string(),
142 codes::VARINT_OVERFLOW => "varint overflow".to_string(),
143 codes::SEQ_UNDERFLOW => "sequence underflow (internal error)".to_string(),
144 codes::INVALID_UTF8 => "invalid UTF-8 in string".to_string(),
145 codes::INVALID_OPTION_DISCRIMINANT => {
146 "invalid Option discriminant (expected 0x00 or 0x01)".to_string()
147 }
148 codes::INVALID_ENUM_DISCRIMINANT => "invalid enum variant discriminant".to_string(),
149 codes::UNSUPPORTED => "unsupported operation".to_string(),
150 _ => format!("unknown error code {}", code),
151 };
152 Self {
153 code,
154 pos,
155 message,
156 source_bytes: None,
157 }
158 }
159}
160
161#[derive(Debug)]
163pub enum SerializeError {
164 BufferTooSmall,
166 Custom(String),
168}
169
170impl fmt::Display for SerializeError {
171 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
172 match self {
173 SerializeError::BufferTooSmall => write!(f, "Buffer too small for serialized data"),
174 SerializeError::Custom(msg) => write!(f, "{}", msg),
175 }
176 }
177}
178
179impl std::error::Error for SerializeError {}
180
181#[cfg(feature = "pretty-errors")]
182impl miette::Diagnostic for SerializeError {}