/rust/registry/src/index.crates.io-1949cf8c6b5b557f/prost-0.14.3/src/error.rs
Line | Count | Source |
1 | | //! Protobuf encoding and decoding errors. |
2 | | |
3 | | use crate::encoding::WireType; |
4 | | use alloc::borrow::Cow; |
5 | | #[cfg(not(feature = "std"))] |
6 | | use alloc::boxed::Box; |
7 | | #[cfg(not(feature = "std"))] |
8 | | use alloc::string::String; |
9 | | #[cfg(not(feature = "std"))] |
10 | | use alloc::vec::Vec; |
11 | | use core::fmt; |
12 | | |
13 | | /// A Protobuf message decoding error. |
14 | | /// |
15 | | /// `DecodeError` indicates that the input buffer does not contain a valid |
16 | | /// Protobuf message. The error details should be considered 'best effort': in |
17 | | /// general it is not possible to exactly pinpoint why data is malformed. |
18 | | #[derive(Clone, PartialEq, Eq)] |
19 | | pub struct DecodeError { |
20 | | inner: Box<Inner>, |
21 | | } |
22 | | |
23 | | #[derive(Clone, PartialEq, Eq)] |
24 | | struct Inner { |
25 | | /// A 'best effort' root cause description. |
26 | | description: DecodeErrorKind, |
27 | | /// A stack of (message, field) name pairs, which identify the specific |
28 | | /// message type and field where decoding failed. The stack contains an |
29 | | /// entry per level of nesting. |
30 | | stack: Vec<(&'static str, &'static str)>, |
31 | | } |
32 | | |
33 | | impl DecodeError { |
34 | | /// Creates a new `DecodeError` with a 'best effort' root cause description. |
35 | | /// |
36 | | /// Meant to be used only by `Message` implementations. |
37 | | #[deprecated( |
38 | | since = "0.14.2", |
39 | | note = "This function was meant for internal use only. Because of `doc(hidden)` it was publicly available and it is actually used by users. The prost project intents to remove this function in the next breaking release." |
40 | | )] |
41 | | #[cold] |
42 | | #[doc(hidden)] |
43 | 0 | pub fn new(description: impl Into<Cow<'static, str>>) -> DecodeError { |
44 | 0 | DecodeErrorKind::Other { |
45 | 0 | description: description.into(), |
46 | 0 | } |
47 | 0 | .into() |
48 | 0 | } |
49 | | |
50 | | /// Creates a new `DecodeError` with a DecodeErrorKind::UnexpectedTypeUrl. |
51 | | /// |
52 | | /// Must only be used by `prost_types::Any` implementation. |
53 | | #[doc(hidden)] |
54 | | #[cold] |
55 | 0 | pub fn new_unexpected_type_url( |
56 | 0 | actual: impl Into<String>, |
57 | 0 | expected: impl Into<String>, |
58 | 0 | ) -> DecodeError { |
59 | 0 | DecodeErrorKind::UnexpectedTypeUrl { |
60 | 0 | actual: actual.into(), |
61 | 0 | expected: expected.into(), |
62 | 0 | } |
63 | 0 | .into() |
64 | 0 | } |
65 | | |
66 | | /// Pushes a (message, field) name location pair on to the location stack. |
67 | | /// |
68 | | /// Meant to be used only by `Message` implementations. |
69 | | #[doc(hidden)] |
70 | 0 | pub fn push(&mut self, message: &'static str, field: &'static str) { |
71 | 0 | self.inner.stack.push((message, field)); |
72 | 0 | } |
73 | | } |
74 | | |
75 | | impl fmt::Debug for DecodeError { |
76 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
77 | 0 | f.debug_struct("DecodeError") |
78 | 0 | .field("description", &self.inner.description) |
79 | 0 | .field("stack", &self.inner.stack) |
80 | 0 | .finish() |
81 | 0 | } |
82 | | } |
83 | | |
84 | | impl fmt::Display for DecodeError { |
85 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
86 | 0 | f.write_str("failed to decode Protobuf message: ")?; |
87 | 0 | for &(message, field) in &self.inner.stack { |
88 | 0 | write!(f, "{message}.{field}: ")?; |
89 | | } |
90 | 0 | write!(f, "{}", self.inner.description) |
91 | 0 | } |
92 | | } |
93 | | |
94 | | impl From<DecodeErrorKind> for DecodeError { |
95 | 0 | fn from(description: DecodeErrorKind) -> Self { |
96 | 0 | DecodeError { |
97 | 0 | inner: Box::new(Inner { |
98 | 0 | description, |
99 | 0 | stack: Vec::new(), |
100 | 0 | }), |
101 | 0 | } |
102 | 0 | } |
103 | | } |
104 | | |
105 | | #[derive(Clone, Debug, PartialEq, Eq)] |
106 | | pub(crate) enum DecodeErrorKind { |
107 | | /// Length delimiter exceeds maximum usize value |
108 | | LengthDelimiterTooLarge, |
109 | | /// Invalid varint |
110 | | InvalidVarint, |
111 | | #[cfg(not(feature = "no-recursion-limit"))] |
112 | | /// Recursion limit reached |
113 | | RecursionLimitReached, |
114 | | /// Invalid wire type value |
115 | | InvalidWireType { value: u64 }, |
116 | | /// Invalid key value |
117 | | InvalidKey { key: u64 }, |
118 | | /// Invalid tag value: 0 |
119 | | InvalidTag, |
120 | | /// Invalid wire type |
121 | | UnexpectedWireType { |
122 | | actual: WireType, |
123 | | expected: WireType, |
124 | | }, |
125 | | /// Buffer underflow |
126 | | BufferUnderflow, |
127 | | /// Delimited length exceeded |
128 | | DelimitedLengthExceeded, |
129 | | /// Unexpected end group tag |
130 | | UnexpectedEndGroupTag, |
131 | | /// Invalid string value: data is not UTF-8 encoded |
132 | | InvalidString, |
133 | | /// Unexpected type URL |
134 | | UnexpectedTypeUrl { actual: String, expected: String }, |
135 | | /// A textual description of a problem |
136 | | Other { description: Cow<'static, str> }, |
137 | | } |
138 | | |
139 | | impl fmt::Display for DecodeErrorKind { |
140 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
141 | 0 | match self { |
142 | | Self::LengthDelimiterTooLarge => { |
143 | 0 | write!(f, "length delimiter exceeds maximum usize value") |
144 | | } |
145 | 0 | Self::InvalidVarint => write!(f, "invalid varint"), |
146 | | #[cfg(not(feature = "no-recursion-limit"))] |
147 | 0 | Self::RecursionLimitReached => write!(f, "recursion limit reached"), |
148 | 0 | Self::InvalidWireType { value } => write!(f, "invalid wire type value: {value}"), |
149 | 0 | Self::InvalidKey { key } => write!(f, "invalid key value: {key}"), |
150 | 0 | Self::InvalidTag => write!(f, "invalid tag value: 0"), |
151 | 0 | Self::UnexpectedWireType { actual, expected } => { |
152 | 0 | write!(f, "invalid wire type: {actual:?} (expected {expected:?})") |
153 | | } |
154 | 0 | Self::BufferUnderflow => write!(f, "buffer underflow"), |
155 | 0 | Self::DelimitedLengthExceeded => write!(f, "delimited length exceeded"), |
156 | 0 | Self::UnexpectedEndGroupTag => write!(f, "unexpected end group tag"), |
157 | | Self::InvalidString => { |
158 | 0 | write!(f, "invalid string value: data is not UTF-8 encoded") |
159 | | } |
160 | 0 | Self::UnexpectedTypeUrl { actual, expected } => { |
161 | 0 | write!(f, "unexpected type URL.type_url: expected type URL: \"{expected}\" (got: \"{actual}\")") |
162 | | } |
163 | 0 | Self::Other { description } => { |
164 | 0 | write!(f, "{description}") |
165 | | } |
166 | | } |
167 | 0 | } |
168 | | } |
169 | | |
170 | | impl core::error::Error for DecodeError {} |
171 | | |
172 | | #[cfg(feature = "std")] |
173 | | impl From<DecodeError> for std::io::Error { |
174 | 0 | fn from(error: DecodeError) -> std::io::Error { |
175 | 0 | std::io::Error::new(std::io::ErrorKind::InvalidData, error) |
176 | 0 | } |
177 | | } |
178 | | |
179 | | /// A Protobuf message encoding error. |
180 | | /// |
181 | | /// `EncodeError` always indicates that a message failed to encode because the |
182 | | /// provided buffer had insufficient capacity. Message encoding is otherwise |
183 | | /// infallible. |
184 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
185 | | pub struct EncodeError { |
186 | | required: usize, |
187 | | remaining: usize, |
188 | | } |
189 | | |
190 | | impl EncodeError { |
191 | | /// Creates a new `EncodeError`. |
192 | 0 | pub(crate) fn new(required: usize, remaining: usize) -> EncodeError { |
193 | 0 | EncodeError { |
194 | 0 | required, |
195 | 0 | remaining, |
196 | 0 | } |
197 | 0 | } |
198 | | |
199 | | /// Returns the required buffer capacity to encode the message. |
200 | 0 | pub fn required_capacity(&self) -> usize { |
201 | 0 | self.required |
202 | 0 | } |
203 | | |
204 | | /// Returns the remaining length in the provided buffer at the time of encoding. |
205 | 0 | pub fn remaining(&self) -> usize { |
206 | 0 | self.remaining |
207 | 0 | } |
208 | | } |
209 | | |
210 | | impl fmt::Display for EncodeError { |
211 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
212 | 0 | write!( |
213 | 0 | f, |
214 | | "failed to encode Protobuf message; insufficient buffer capacity (required: {}, remaining: {})", |
215 | | self.required, self.remaining |
216 | | ) |
217 | 0 | } |
218 | | } |
219 | | |
220 | | impl core::error::Error for EncodeError {} |
221 | | |
222 | | #[cfg(feature = "std")] |
223 | | impl From<EncodeError> for std::io::Error { |
224 | 0 | fn from(error: EncodeError) -> std::io::Error { |
225 | 0 | std::io::Error::new(std::io::ErrorKind::InvalidInput, error) |
226 | 0 | } |
227 | | } |
228 | | |
229 | | /// An error indicating that an unknown enumeration value was encountered. |
230 | | /// |
231 | | /// The Protobuf spec mandates that enumeration value sets are ‘open’, so this |
232 | | /// error's value represents an integer value unrecognized by the |
233 | | /// presently used enum definition. |
234 | | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
235 | | pub struct UnknownEnumValue(pub i32); |
236 | | |
237 | | impl fmt::Display for UnknownEnumValue { |
238 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
239 | 0 | write!(f, "unknown enumeration value {}", self.0) |
240 | 0 | } |
241 | | } |
242 | | |
243 | | impl core::error::Error for UnknownEnumValue {} |
244 | | |
245 | | #[cfg(test)] |
246 | | mod test { |
247 | | use super::*; |
248 | | |
249 | | #[test] |
250 | | fn test_push() { |
251 | | let mut decode_error = DecodeError::from(DecodeErrorKind::InvalidVarint); |
252 | | decode_error.push("Foo bad", "bar.foo"); |
253 | | decode_error.push("Baz bad", "bar.baz"); |
254 | | |
255 | | assert_eq!( |
256 | | decode_error.to_string(), |
257 | | "failed to decode Protobuf message: Foo bad.bar.foo: Baz bad.bar.baz: invalid varint" |
258 | | ); |
259 | | } |
260 | | |
261 | | #[cfg(feature = "std")] |
262 | | #[test] |
263 | | fn test_into_std_io_error() { |
264 | | let decode_error = DecodeError::from(DecodeErrorKind::InvalidVarint); |
265 | | let std_io_error = std::io::Error::from(decode_error); |
266 | | |
267 | | assert_eq!(std_io_error.kind(), std::io::ErrorKind::InvalidData); |
268 | | assert_eq!( |
269 | | std_io_error.to_string(), |
270 | | "failed to decode Protobuf message: invalid varint" |
271 | | ); |
272 | | } |
273 | | } |