serde_scale/
err.rs

1// Copyright (C) 2020 Stephane Raux. Distributed under the zlib license.
2
3#[cfg(feature = "alloc")]
4use alloc::string::ToString;
5use core::fmt::{self, Debug, Display};
6
7/// Serialization errors
8#[derive(Debug)]
9pub enum Error<E> {
10    /// SCALE does not specify how to serialize floating point values
11    FloatingPointUnsupported,
12    /// SCALE limits enums to 255 variants
13    TooManyVariants {
14        enum_name: &'static str,
15        variant_name: &'static str,
16        variant_index: u32,
17    },
18    /// SCALE requires knowing the length of collections
19    LengthNeeded,
20    /// SCALE requires knowing the type of the data being deserialized
21    TypeMustBeKnown,
22    /// A boolean value (0 or 1) was expected but another byte was found
23    ExpectedBoolean {
24        found: u8,
25    },
26    /// Invalid character found. Characters must be UTF-32 code points.
27    InvalidCharacter {
28        found: u32,
29    },
30    /// This implementation limits collections to 2^64 elements
31    CollectionTooLargeToSerialize {
32        len: usize,
33    },
34    /// This implementation limits collections to 2^64 elements
35    CollectionTooLargeToDeserialize,
36    /// Invalid Unicode was found in a string
37    InvalidUnicode(core::str::Utf8Error),
38    /// An option was expected but the discriminant is invalid
39    InvalidOption {
40        found_discriminant: u8,
41    },
42    /// I/O error from the underlying reader or writer
43    Io(E),
44    /// Other error the serializer or deserializer might encounter
45    Other(OtherError),
46}
47
48impl<E> From<E> for Error<E> {
49    fn from(e: E) -> Self {
50        Error::Io(e)
51    }
52}
53
54impl<E: Display> Display for Error<E> {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match self {
57            Error::FloatingPointUnsupported => {
58                write!(f, "Floating point values are not supported by the SCALE encoding")
59            }
60            Error::TooManyVariants { enum_name, variant_name, variant_index } => {
61                write!(f, "Variant {}::{} has index {} but the SCALE encoding limits enumerations \
62                    to 255 variants", enum_name, variant_name, variant_index)
63            }
64            Error::LengthNeeded => {
65                write!(f, "Sequence length unknown but the SCALE encoding requires to know it")
66            }
67            Error::TypeMustBeKnown => {
68                write!(f, "Type unknown but the SCALE encoding requires to know it")
69            }
70            Error::ExpectedBoolean { found } => {
71                write!(f, "Expected boolean (0 or 1), found {}", found)
72            }
73            Error::InvalidCharacter { found } => {
74                write!(f, "{} is an invalid UTF-32 codepoint", found)
75            }
76            Error::CollectionTooLargeToSerialize { len } => {
77                write!(f, "Found a collection of {} elements but this implementation limits \
78                    collections to 2^64 elements", len)
79            }
80            Error::CollectionTooLargeToDeserialize => {
81                write!(f, "Collections of more than 2^64 elements are not supported")
82            }
83            Error::InvalidUnicode(e) => {
84                write!(f, "Invalid Unicode in string: {}", e)
85            }
86            Error::InvalidOption { found_discriminant } => {
87                write!(f, "Invalid option. Expected a discriminant of 0 or 1 but found {}",
88                    found_discriminant)
89            }
90            Error::Io(e) => {
91                write!(f, "I/O error: {}", e)
92            }
93            Error::Other(e) => write!(f, "{}", e),
94        }
95    }
96}
97
98#[cfg(feature = "std")]
99impl<E: Debug + Display> std::error::Error for Error<E> {
100    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
101        match self {
102            Error::InvalidUnicode(e) => Some(e),
103            Error::Io(_) => {
104                // Ideally the bound would be `E: std::error::Error + 'static` and the inner error
105                // could be returned but doing so leads to a world of sadness when a dependency tree
106                // turns on `serde/std` without turning on the `std` feature of crates defining
107                // error types.
108                None
109            }
110            Error::FloatingPointUnsupported
111            | Error::TooManyVariants { .. }
112            | Error::LengthNeeded
113            | Error::TypeMustBeKnown
114            | Error::ExpectedBoolean { .. }
115            | Error::InvalidCharacter { .. }
116            | Error::CollectionTooLargeToSerialize { .. }
117            | Error::CollectionTooLargeToDeserialize
118            | Error::InvalidOption { .. }
119            | Error::Other(_) => None,
120        }
121    }
122}
123
124#[cfg(not(feature = "std"))]
125impl<E: Debug + Display> serde::ser::StdError for Error<E> {}
126
127impl<E: Debug + Display> serde::ser::Error for Error<E> {
128    fn custom<T: Display>(msg: T) -> Self {
129        #[cfg(feature = "alloc")]
130        {
131            Error::Other(msg.to_string().into())
132        }
133        #[cfg(not(feature = "alloc"))]
134        {
135            let _ = msg;
136            Error::Other("Custom error".into())
137        }
138    }
139}
140
141impl<E: Debug + Display> serde::de::Error for Error<E> {
142    fn custom<T: Display>(msg: T) -> Self {
143        serde::ser::Error::custom(msg)
144    }
145}
146
147pub use other_error::OtherError;
148
149#[cfg(feature = "alloc")]
150mod other_error {
151    use alloc::string::String;
152
153    #[derive(Clone, Debug, Eq, PartialEq)]
154    pub struct OtherError(String);
155
156    impl OtherError {
157        pub fn as_str(&self) -> &str {
158            &self.0
159        }
160    }
161
162    impl From<String> for OtherError {
163        fn from(s: String) -> Self {
164            Self(s)
165        }
166    }
167
168    impl From<&str> for OtherError {
169        fn from(s: &str) -> Self {
170            Self(s.into())
171        }
172    }
173}
174
175#[cfg(not(feature = "alloc"))]
176mod other_error {
177    #[derive(Clone, Debug, Eq, PartialEq)]
178    pub struct OtherError(&'static str);
179
180    impl OtherError {
181        pub fn as_str(&self) -> &str {
182            self.0
183        }
184    }
185
186    impl From<&'static str> for OtherError {
187        fn from(s: &'static str) -> Self {
188            Self(s)
189        }
190    }
191}
192
193impl Display for OtherError {
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        f.write_str(self.as_str())
196    }
197}