prost/
error.rs

1//! Protobuf encoding and decoding errors.
2
3use alloc::borrow::Cow;
4#[cfg(not(feature = "std"))]
5use alloc::boxed::Box;
6#[cfg(not(feature = "std"))]
7use alloc::vec::Vec;
8
9use core::fmt;
10
11/// A Protobuf message decoding error.
12///
13/// `DecodeError` indicates that the input buffer does not contain a valid
14/// Protobuf message. The error details should be considered 'best effort': in
15/// general it is not possible to exactly pinpoint why data is malformed.
16#[derive(Clone, PartialEq, Eq)]
17pub struct DecodeError {
18    inner: Box<Inner>,
19}
20
21#[derive(Clone, PartialEq, Eq)]
22struct Inner {
23    /// A 'best effort' root cause description.
24    description: Cow<'static, str>,
25    /// A stack of (message, field) name pairs, which identify the specific
26    /// message type and field where decoding failed. The stack contains an
27    /// entry per level of nesting.
28    stack: Vec<(&'static str, &'static str)>,
29}
30
31impl DecodeError {
32    /// Creates a new `DecodeError` with a 'best effort' root cause description.
33    ///
34    /// Meant to be used only by `Message` implementations.
35    #[doc(hidden)]
36    #[cold]
37    pub fn new(description: impl Into<Cow<'static, str>>) -> DecodeError {
38        DecodeError {
39            inner: Box::new(Inner {
40                description: description.into(),
41                stack: Vec::new(),
42            }),
43        }
44    }
45
46    /// Pushes a (message, field) name location pair on to the location stack.
47    ///
48    /// Meant to be used only by `Message` implementations.
49    #[doc(hidden)]
50    pub fn push(&mut self, message: &'static str, field: &'static str) {
51        self.inner.stack.push((message, field));
52    }
53}
54
55impl fmt::Debug for DecodeError {
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.debug_struct("DecodeError")
58            .field("description", &self.inner.description)
59            .field("stack", &self.inner.stack)
60            .finish()
61    }
62}
63
64impl fmt::Display for DecodeError {
65    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66        f.write_str("failed to decode Protobuf message: ")?;
67        for &(message, field) in &self.inner.stack {
68            write!(f, "{}.{}: ", message, field)?;
69        }
70        f.write_str(&self.inner.description)
71    }
72}
73
74#[cfg(feature = "std")]
75impl std::error::Error for DecodeError {}
76
77#[cfg(feature = "std")]
78impl From<DecodeError> for std::io::Error {
79    fn from(error: DecodeError) -> std::io::Error {
80        std::io::Error::new(std::io::ErrorKind::InvalidData, error)
81    }
82}
83
84/// A Protobuf message encoding error.
85///
86/// `EncodeError` always indicates that a message failed to encode because the
87/// provided buffer had insufficient capacity. Message encoding is otherwise
88/// infallible.
89#[derive(Copy, Clone, Debug, PartialEq, Eq)]
90pub struct EncodeError {
91    required: usize,
92    remaining: usize,
93}
94
95impl EncodeError {
96    /// Creates a new `EncodeError`.
97    pub(crate) fn new(required: usize, remaining: usize) -> EncodeError {
98        EncodeError {
99            required,
100            remaining,
101        }
102    }
103
104    /// Returns the required buffer capacity to encode the message.
105    pub fn required_capacity(&self) -> usize {
106        self.required
107    }
108
109    /// Returns the remaining length in the provided buffer at the time of encoding.
110    pub fn remaining(&self) -> usize {
111        self.remaining
112    }
113}
114
115impl fmt::Display for EncodeError {
116    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117        write!(
118            f,
119            "failed to encode Protobuf message; insufficient buffer capacity (required: {}, remaining: {})",
120            self.required, self.remaining
121        )
122    }
123}
124
125#[cfg(feature = "std")]
126impl std::error::Error for EncodeError {}
127
128#[cfg(feature = "std")]
129impl From<EncodeError> for std::io::Error {
130    fn from(error: EncodeError) -> std::io::Error {
131        std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
132    }
133}
134
135/// An error indicating that an unknown enumeration value was encountered.
136///
137/// The Protobuf spec mandates that enumeration value sets are ‘open’, so this
138/// error's value represents an integer value unrecognized by the
139/// presently used enum definition.
140#[derive(Copy, Clone, Debug, PartialEq, Eq)]
141pub struct UnknownEnumValue(pub i32);
142
143impl fmt::Display for UnknownEnumValue {
144    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145        write!(f, "unknown enumeration value {}", self.0)
146    }
147}
148
149#[cfg(feature = "std")]
150impl std::error::Error for UnknownEnumValue {}
151
152#[cfg(test)]
153mod test {
154    use super::*;
155
156    #[test]
157    fn test_push() {
158        let mut decode_error = DecodeError::new("something failed");
159        decode_error.push("Foo bad", "bar.foo");
160        decode_error.push("Baz bad", "bar.baz");
161
162        assert_eq!(
163            decode_error.to_string(),
164            "failed to decode Protobuf message: Foo bad.bar.foo: Baz bad.bar.baz: something failed"
165        );
166    }
167
168    #[cfg(feature = "std")]
169    #[test]
170    fn test_into_std_io_error() {
171        let decode_error = DecodeError::new("something failed");
172        let std_io_error = std::io::Error::from(decode_error);
173
174        assert_eq!(std_io_error.kind(), std::io::ErrorKind::InvalidData);
175        assert_eq!(
176            std_io_error.to_string(),
177            "failed to decode Protobuf message: something failed"
178        );
179    }
180}