use crate::encoding::WireType;
use alloc::borrow::Cow;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::string::String;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use core::fmt;
#[derive(Clone, PartialEq, Eq)]
pub struct DecodeError {
inner: Box<Inner>,
}
#[derive(Clone, PartialEq, Eq)]
struct Inner {
description: DecodeErrorKind,
stack: Vec<(&'static str, &'static str)>,
}
impl DecodeError {
#[deprecated(
since = "0.14.2",
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."
)]
#[cold]
#[doc(hidden)]
pub fn new(description: impl Into<Cow<'static, str>>) -> DecodeError {
DecodeErrorKind::Other {
description: description.into(),
}
.into()
}
#[doc(hidden)]
#[cold]
pub fn new_unexpected_type_url(
actual: impl Into<String>,
expected: impl Into<String>,
) -> DecodeError {
DecodeErrorKind::UnexpectedTypeUrl {
actual: actual.into(),
expected: expected.into(),
}
.into()
}
#[doc(hidden)]
pub fn push(&mut self, message: &'static str, field: &'static str) {
self.inner.stack.push((message, field));
}
}
impl fmt::Debug for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("DecodeError")
.field("description", &self.inner.description)
.field("stack", &self.inner.stack)
.finish()
}
}
impl fmt::Display for DecodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("failed to decode Protobuf message: ")?;
for &(message, field) in &self.inner.stack {
write!(f, "{message}.{field}: ")?;
}
write!(f, "{}", self.inner.description)
}
}
impl From<DecodeErrorKind> for DecodeError {
fn from(description: DecodeErrorKind) -> Self {
DecodeError {
inner: Box::new(Inner {
description,
stack: Vec::new(),
}),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum DecodeErrorKind {
LengthDelimiterTooLarge,
InvalidVarint,
#[cfg(not(feature = "no-recursion-limit"))]
RecursionLimitReached,
InvalidWireType { value: u64 },
InvalidKey { key: u64 },
InvalidTag,
UnexpectedWireType {
actual: WireType,
expected: WireType,
},
BufferUnderflow,
DelimitedLengthExceeded,
UnexpectedEndGroupTag,
InvalidString,
UnexpectedTypeUrl { actual: String, expected: String },
Other { description: Cow<'static, str> },
}
impl fmt::Display for DecodeErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::LengthDelimiterTooLarge => {
write!(f, "length delimiter exceeds maximum usize value")
}
Self::InvalidVarint => write!(f, "invalid varint"),
#[cfg(not(feature = "no-recursion-limit"))]
Self::RecursionLimitReached => write!(f, "recursion limit reached"),
Self::InvalidWireType { value } => write!(f, "invalid wire type value: {value}"),
Self::InvalidKey { key } => write!(f, "invalid key value: {key}"),
Self::InvalidTag => write!(f, "invalid tag value: 0"),
Self::UnexpectedWireType { actual, expected } => {
write!(f, "invalid wire type: {actual:?} (expected {expected:?})")
}
Self::BufferUnderflow => write!(f, "buffer underflow"),
Self::DelimitedLengthExceeded => write!(f, "delimited length exceeded"),
Self::UnexpectedEndGroupTag => write!(f, "unexpected end group tag"),
Self::InvalidString => {
write!(f, "invalid string value: data is not UTF-8 encoded")
}
Self::UnexpectedTypeUrl { actual, expected } => {
write!(f, "unexpected type URL.type_url: expected type URL: \"{expected}\" (got: \"{actual}\")")
}
Self::Other { description } => {
write!(f, "{description}")
}
}
}
}
impl core::error::Error for DecodeError {}
#[cfg(feature = "std")]
impl From<DecodeError> for std::io::Error {
fn from(error: DecodeError) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::InvalidData, error)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct EncodeError {
required: usize,
remaining: usize,
}
impl EncodeError {
pub(crate) fn new(required: usize, remaining: usize) -> EncodeError {
EncodeError {
required,
remaining,
}
}
pub fn required_capacity(&self) -> usize {
self.required
}
pub fn remaining(&self) -> usize {
self.remaining
}
}
impl fmt::Display for EncodeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"failed to encode Protobuf message; insufficient buffer capacity (required: {}, remaining: {})",
self.required, self.remaining
)
}
}
impl core::error::Error for EncodeError {}
#[cfg(feature = "std")]
impl From<EncodeError> for std::io::Error {
fn from(error: EncodeError) -> std::io::Error {
std::io::Error::new(std::io::ErrorKind::InvalidInput, error)
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct UnknownEnumValue(pub i32);
impl fmt::Display for UnknownEnumValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown enumeration value {}", self.0)
}
}
impl core::error::Error for UnknownEnumValue {}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_push() {
let mut decode_error = DecodeError::from(DecodeErrorKind::InvalidVarint);
decode_error.push("Foo bad", "bar.foo");
decode_error.push("Baz bad", "bar.baz");
assert_eq!(
decode_error.to_string(),
"failed to decode Protobuf message: Foo bad.bar.foo: Baz bad.bar.baz: invalid varint"
);
}
#[cfg(feature = "std")]
#[test]
fn test_into_std_io_error() {
let decode_error = DecodeError::from(DecodeErrorKind::InvalidVarint);
let std_io_error = std::io::Error::from(decode_error);
assert_eq!(std_io_error.kind(), std::io::ErrorKind::InvalidData);
assert_eq!(
std_io_error.to_string(),
"failed to decode Protobuf message: invalid varint"
);
}
}