planus/
errors.rs

1/// The main error type for Planus
2#[derive(Copy, Clone, Debug)]
3pub struct Error {
4    /// The location of the error
5    pub source_location: ErrorLocation,
6    /// The kind of error
7    pub error_kind: ErrorKind,
8}
9
10impl core::fmt::Display for Error {
11    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
12        write!(f, "In {}: {}", self.source_location, self.error_kind)
13    }
14}
15
16#[cfg(feature = "std")]
17impl std::error::Error for Error {
18    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
19        Some(&self.error_kind)
20    }
21}
22
23/// The possible errors in planus when reading data from a serialized buffer.
24#[derive(Copy, Clone, Debug)]
25#[non_exhaustive]
26pub enum ErrorKind {
27    /// The offset was out of bounds.
28    InvalidOffset,
29    /// The buffer was too short while validating a length field.
30    InvalidLength,
31    /// A vector of unions had different lengths for the tag and value vectors.
32    UnionVectorLengthsMismatched {
33        /// The length of the tags vector
34        tags_len: usize,
35        /// The length of the values vector
36        values_len: usize,
37    },
38    /// An enum contained an unknown value. For forward compatibility this
39    /// error should be handled appropriately.
40    UnknownEnumTag {
41        /// The enum value that wasn't recognized.
42        source: UnknownEnumTagKind,
43    },
44    /// An union contained an unknown variant. For forward compatibility this
45    /// error should be handled appropriately.
46    UnknownUnionTag {
47        /// The union tag that wasn't recognized.
48        tag: u8,
49    },
50    /// A vtable had an invalid length (too large, too small or unaligned).
51    InvalidVtableLength {
52        /// The length of the vtable.
53        length: u16,
54    },
55    /// A string contained invalid utf-8.
56    InvalidUtf8 {
57        /// The utf-8 error triggered by the string.
58        source: core::str::Utf8Error,
59    },
60    /// A required field was missing.
61    MissingRequired,
62    /// A string null terminator was missing.
63    MissingNullTerminator,
64}
65
66impl core::fmt::Display for ErrorKind {
67    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
68        match self {
69            ErrorKind::InvalidOffset => write!(f, "Invalid offset"),
70            ErrorKind::InvalidLength => write!(f, "Invalid length"),
71            ErrorKind::UnionVectorLengthsMismatched {
72                tags_len,
73                values_len,
74            } => {
75                write!(f, "Mismatched lengths between tag and value vectors. Length of tags = {tags_len}, length of values = {values_len}")
76            }
77            ErrorKind::UnknownEnumTag { source } => source.fmt(f),
78            ErrorKind::UnknownUnionTag { tag } => write!(f, "Unknown union (tag = {tag})"),
79            ErrorKind::InvalidVtableLength { length } => {
80                write!(f, "Invalid vtable length (length = {length})")
81            }
82            ErrorKind::InvalidUtf8 { source } => write!(f, "Invalid utf-8: {source}"),
83            ErrorKind::MissingRequired => write!(f, "Missing required field"),
84            ErrorKind::MissingNullTerminator => write!(f, "Missing null terminator"),
85        }
86    }
87}
88
89#[cfg(feature = "std")]
90impl std::error::Error for ErrorKind {
91    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
92        match self {
93            ErrorKind::InvalidOffset => None,
94            ErrorKind::InvalidLength => None,
95            ErrorKind::UnionVectorLengthsMismatched { .. } => None,
96            ErrorKind::UnknownEnumTag { source } => Some(source),
97            ErrorKind::UnknownUnionTag { .. } => None,
98            ErrorKind::InvalidVtableLength { .. } => None,
99            ErrorKind::InvalidUtf8 { source } => Some(source),
100            ErrorKind::MissingRequired => None,
101            ErrorKind::MissingNullTerminator => None,
102        }
103    }
104}
105
106impl From<UnknownEnumTagKind> for ErrorKind {
107    fn from(source: UnknownEnumTagKind) -> Self {
108        ErrorKind::UnknownEnumTag { source }
109    }
110}
111
112impl From<core::str::Utf8Error> for ErrorKind {
113    fn from(source: core::str::Utf8Error) -> Self {
114        ErrorKind::InvalidUtf8 { source }
115    }
116}
117
118#[derive(Clone, Debug)]
119/// Information about an unrecognized enum tag.
120///
121/// In order to be forward compatible [`Result`]s with this error variant should
122/// be handled gracefully.
123pub struct UnknownEnumTag {
124    /// The location of the unknown tag.
125    pub source_location: ErrorLocation,
126    /// The unknown tag.
127    pub error_kind: UnknownEnumTagKind,
128}
129
130impl core::fmt::Display for UnknownEnumTag {
131    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
132        write!(f, "In {}: {}", self.source_location, self.error_kind)
133    }
134}
135
136#[cfg(feature = "std")]
137impl std::error::Error for UnknownEnumTag {
138    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
139        Some(&self.error_kind)
140    }
141}
142
143#[derive(Copy, Clone, Debug)]
144/// The value of an unknown enum tag.
145pub struct UnknownEnumTagKind {
146    /// The unknown tag.
147    pub tag: i128,
148}
149
150impl core::fmt::Display for UnknownEnumTagKind {
151    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
152        write!(f, "Unknown enum (tag = {})", self.tag)
153    }
154}
155
156#[cfg(feature = "std")]
157impl std::error::Error for UnknownEnumTagKind {}
158
159#[derive(Copy, Clone, Debug)]
160/// The location of the error in both the generated code and the binary data
161/// where it was encountered.
162pub struct ErrorLocation {
163    /// The flatbuffers type where the error was encountered.
164    pub type_: &'static str,
165    /// The generated method where the error was encountered.
166    pub method: &'static str,
167    /// Offset into the flatbuffers buffer where the error was encountered.
168    pub byte_offset: usize,
169}
170
171impl core::fmt::Display for ErrorLocation {
172    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173        if self.byte_offset != usize::MAX {
174            write!(
175                f,
176                "<{}@{:x}>::{}()",
177                self.type_, self.byte_offset, self.method,
178            )
179        } else {
180            write!(f, "<{}>::{}()", self.type_, self.method,)
181        }
182    }
183}
184
185impl From<UnknownEnumTag> for Error {
186    fn from(error: UnknownEnumTag) -> Self {
187        Self {
188            source_location: error.source_location,
189            error_kind: error.error_kind.into(),
190        }
191    }
192}
193
194impl From<core::convert::Infallible> for Error {
195    fn from(value: core::convert::Infallible) -> Self {
196        match value {}
197    }
198}
199
200impl UnknownEnumTagKind {
201    /// Helper function that adds an error location to this error.
202    pub fn with_error_location(
203        self,
204        type_: &'static str,
205        method: &'static str,
206        byte_offset: usize,
207    ) -> UnknownEnumTag {
208        UnknownEnumTag {
209            source_location: ErrorLocation {
210                type_,
211                method,
212                byte_offset,
213            },
214            error_kind: self,
215        }
216    }
217}
218
219impl ErrorKind {
220    /// Helper function that adds an error location to this error.
221    pub fn with_error_location(
222        self,
223        type_: &'static str,
224        method: &'static str,
225        byte_offset: usize,
226    ) -> Error {
227        Error {
228            source_location: ErrorLocation {
229                type_,
230                method,
231                byte_offset,
232            },
233            error_kind: self,
234        }
235    }
236}