Coverage Report

Created: 2026-06-30 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}