summaryrefslogtreecommitdiffstats
path: root/vendor/serde_json/src/error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/serde_json/src/error.rs')
-rw-r--r--vendor/serde_json/src/error.rs445
1 files changed, 445 insertions, 0 deletions
diff --git a/vendor/serde_json/src/error.rs b/vendor/serde_json/src/error.rs
new file mode 100644
index 000000000..1875ef08b
--- /dev/null
+++ b/vendor/serde_json/src/error.rs
@@ -0,0 +1,445 @@
+//! When serializing or deserializing JSON goes wrong.
+
+use crate::io;
+use alloc::boxed::Box;
+use alloc::string::{String, ToString};
+use core::fmt::{self, Debug, Display};
+use core::result;
+use core::str::FromStr;
+use serde::{de, ser};
+#[cfg(feature = "std")]
+use std::error;
+
+/// This type represents all possible errors that can occur when serializing or
+/// deserializing JSON data.
+pub struct Error {
+ /// This `Box` allows us to keep the size of `Error` as small as possible. A
+ /// larger `Error` type was substantially slower due to all the functions
+ /// that pass around `Result<T, Error>`.
+ err: Box<ErrorImpl>,
+}
+
+/// Alias for a `Result` with the error type `serde_json::Error`.
+pub type Result<T> = result::Result<T, Error>;
+
+impl Error {
+ /// One-based line number at which the error was detected.
+ ///
+ /// Characters in the first line of the input (before the first newline
+ /// character) are in line 1.
+ pub fn line(&self) -> usize {
+ self.err.line
+ }
+
+ /// One-based column number at which the error was detected.
+ ///
+ /// The first character in the input and any characters immediately
+ /// following a newline character are in column 1.
+ ///
+ /// Note that errors may occur in column 0, for example if a read from an IO
+ /// stream fails immediately following a previously read newline character.
+ pub fn column(&self) -> usize {
+ self.err.column
+ }
+
+ /// Categorizes the cause of this error.
+ ///
+ /// - `Category::Io` - failure to read or write bytes on an IO stream
+ /// - `Category::Syntax` - input that is not syntactically valid JSON
+ /// - `Category::Data` - input data that is semantically incorrect
+ /// - `Category::Eof` - unexpected end of the input data
+ pub fn classify(&self) -> Category {
+ match self.err.code {
+ ErrorCode::Message(_) => Category::Data,
+ ErrorCode::Io(_) => Category::Io,
+ ErrorCode::EofWhileParsingList
+ | ErrorCode::EofWhileParsingObject
+ | ErrorCode::EofWhileParsingString
+ | ErrorCode::EofWhileParsingValue => Category::Eof,
+ ErrorCode::ExpectedColon
+ | ErrorCode::ExpectedListCommaOrEnd
+ | ErrorCode::ExpectedObjectCommaOrEnd
+ | ErrorCode::ExpectedSomeIdent
+ | ErrorCode::ExpectedSomeValue
+ | ErrorCode::InvalidEscape
+ | ErrorCode::InvalidNumber
+ | ErrorCode::NumberOutOfRange
+ | ErrorCode::InvalidUnicodeCodePoint
+ | ErrorCode::ControlCharacterWhileParsingString
+ | ErrorCode::KeyMustBeAString
+ | ErrorCode::LoneLeadingSurrogateInHexEscape
+ | ErrorCode::TrailingComma
+ | ErrorCode::TrailingCharacters
+ | ErrorCode::UnexpectedEndOfHexEscape
+ | ErrorCode::RecursionLimitExceeded => Category::Syntax,
+ }
+ }
+
+ /// Returns true if this error was caused by a failure to read or write
+ /// bytes on an IO stream.
+ pub fn is_io(&self) -> bool {
+ self.classify() == Category::Io
+ }
+
+ /// Returns true if this error was caused by input that was not
+ /// syntactically valid JSON.
+ pub fn is_syntax(&self) -> bool {
+ self.classify() == Category::Syntax
+ }
+
+ /// Returns true if this error was caused by input data that was
+ /// semantically incorrect.
+ ///
+ /// For example, JSON containing a number is semantically incorrect when the
+ /// type being deserialized into holds a String.
+ pub fn is_data(&self) -> bool {
+ self.classify() == Category::Data
+ }
+
+ /// Returns true if this error was caused by prematurely reaching the end of
+ /// the input data.
+ ///
+ /// Callers that process streaming input may be interested in retrying the
+ /// deserialization once more data is available.
+ pub fn is_eof(&self) -> bool {
+ self.classify() == Category::Eof
+ }
+}
+
+/// Categorizes the cause of a `serde_json::Error`.
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub enum Category {
+ /// The error was caused by a failure to read or write bytes on an IO
+ /// stream.
+ Io,
+
+ /// The error was caused by input that was not syntactically valid JSON.
+ Syntax,
+
+ /// The error was caused by input data that was semantically incorrect.
+ ///
+ /// For example, JSON containing a number is semantically incorrect when the
+ /// type being deserialized into holds a String.
+ Data,
+
+ /// The error was caused by prematurely reaching the end of the input data.
+ ///
+ /// Callers that process streaming input may be interested in retrying the
+ /// deserialization once more data is available.
+ Eof,
+}
+
+#[cfg(feature = "std")]
+#[allow(clippy::fallible_impl_from)]
+impl From<Error> for io::Error {
+ /// Convert a `serde_json::Error` into an `io::Error`.
+ ///
+ /// JSON syntax and data errors are turned into `InvalidData` IO errors.
+ /// EOF errors are turned into `UnexpectedEof` IO errors.
+ ///
+ /// ```
+ /// use std::io;
+ ///
+ /// enum MyError {
+ /// Io(io::Error),
+ /// Json(serde_json::Error),
+ /// }
+ ///
+ /// impl From<serde_json::Error> for MyError {
+ /// fn from(err: serde_json::Error) -> MyError {
+ /// use serde_json::error::Category;
+ /// match err.classify() {
+ /// Category::Io => {
+ /// MyError::Io(err.into())
+ /// }
+ /// Category::Syntax | Category::Data | Category::Eof => {
+ /// MyError::Json(err)
+ /// }
+ /// }
+ /// }
+ /// }
+ /// ```
+ fn from(j: Error) -> Self {
+ if let ErrorCode::Io(err) = j.err.code {
+ err
+ } else {
+ match j.classify() {
+ Category::Io => unreachable!(),
+ Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j),
+ Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j),
+ }
+ }
+ }
+}
+
+struct ErrorImpl {
+ code: ErrorCode,
+ line: usize,
+ column: usize,
+}
+
+pub(crate) enum ErrorCode {
+ /// Catchall for syntax error messages
+ Message(Box<str>),
+
+ /// Some IO error occurred while serializing or deserializing.
+ Io(io::Error),
+
+ /// EOF while parsing a list.
+ EofWhileParsingList,
+
+ /// EOF while parsing an object.
+ EofWhileParsingObject,
+
+ /// EOF while parsing a string.
+ EofWhileParsingString,
+
+ /// EOF while parsing a JSON value.
+ EofWhileParsingValue,
+
+ /// Expected this character to be a `':'`.
+ ExpectedColon,
+
+ /// Expected this character to be either a `','` or a `']'`.
+ ExpectedListCommaOrEnd,
+
+ /// Expected this character to be either a `','` or a `'}'`.
+ ExpectedObjectCommaOrEnd,
+
+ /// Expected to parse either a `true`, `false`, or a `null`.
+ ExpectedSomeIdent,
+
+ /// Expected this character to start a JSON value.
+ ExpectedSomeValue,
+
+ /// Invalid hex escape code.
+ InvalidEscape,
+
+ /// Invalid number.
+ InvalidNumber,
+
+ /// Number is bigger than the maximum value of its type.
+ NumberOutOfRange,
+
+ /// Invalid unicode code point.
+ InvalidUnicodeCodePoint,
+
+ /// Control character found while parsing a string.
+ ControlCharacterWhileParsingString,
+
+ /// Object key is not a string.
+ KeyMustBeAString,
+
+ /// Lone leading surrogate in hex escape.
+ LoneLeadingSurrogateInHexEscape,
+
+ /// JSON has a comma after the last value in an array or map.
+ TrailingComma,
+
+ /// JSON has non-whitespace trailing characters after the value.
+ TrailingCharacters,
+
+ /// Unexpected end of hex escape.
+ UnexpectedEndOfHexEscape,
+
+ /// Encountered nesting of JSON maps and arrays more than 128 layers deep.
+ RecursionLimitExceeded,
+}
+
+impl Error {
+ #[cold]
+ pub(crate) fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
+ Error {
+ err: Box::new(ErrorImpl { code, line, column }),
+ }
+ }
+
+ // Not public API. Should be pub(crate).
+ //
+ // Update `eager_json` crate when this function changes.
+ #[doc(hidden)]
+ #[cold]
+ pub fn io(error: io::Error) -> Self {
+ Error {
+ err: Box::new(ErrorImpl {
+ code: ErrorCode::Io(error),
+ line: 0,
+ column: 0,
+ }),
+ }
+ }
+
+ #[cold]
+ pub(crate) fn fix_position<F>(self, f: F) -> Self
+ where
+ F: FnOnce(ErrorCode) -> Error,
+ {
+ if self.err.line == 0 {
+ f(self.err.code)
+ } else {
+ self
+ }
+ }
+}
+
+impl Display for ErrorCode {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ErrorCode::Message(msg) => f.write_str(msg),
+ ErrorCode::Io(err) => Display::fmt(err, f),
+ ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
+ ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
+ ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
+ ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
+ ErrorCode::ExpectedColon => f.write_str("expected `:`"),
+ ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
+ ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
+ ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
+ ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
+ ErrorCode::InvalidEscape => f.write_str("invalid escape"),
+ ErrorCode::InvalidNumber => f.write_str("invalid number"),
+ ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
+ ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
+ ErrorCode::ControlCharacterWhileParsingString => {
+ f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
+ }
+ ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
+ ErrorCode::LoneLeadingSurrogateInHexEscape => {
+ f.write_str("lone leading surrogate in hex escape")
+ }
+ ErrorCode::TrailingComma => f.write_str("trailing comma"),
+ ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
+ ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
+ ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
+ }
+ }
+}
+
+impl serde::de::StdError for Error {
+ #[cfg(feature = "std")]
+ fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+ match &self.err.code {
+ ErrorCode::Io(err) => Some(err),
+ _ => None,
+ }
+ }
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ Display::fmt(&*self.err, f)
+ }
+}
+
+impl Display for ErrorImpl {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.line == 0 {
+ Display::fmt(&self.code, f)
+ } else {
+ write!(
+ f,
+ "{} at line {} column {}",
+ self.code, self.line, self.column
+ )
+ }
+ }
+}
+
+// Remove two layers of verbosity from the debug representation. Humans often
+// end up seeing this representation because it is what unwrap() shows.
+impl Debug for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "Error({:?}, line: {}, column: {})",
+ self.err.code.to_string(),
+ self.err.line,
+ self.err.column
+ )
+ }
+}
+
+impl de::Error for Error {
+ #[cold]
+ fn custom<T: Display>(msg: T) -> Error {
+ make_error(msg.to_string())
+ }
+
+ #[cold]
+ fn invalid_type(unexp: de::Unexpected, exp: &dyn de::Expected) -> Self {
+ if let de::Unexpected::Unit = unexp {
+ Error::custom(format_args!("invalid type: null, expected {}", exp))
+ } else {
+ Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
+ }
+ }
+}
+
+impl ser::Error for Error {
+ #[cold]
+ fn custom<T: Display>(msg: T) -> Error {
+ make_error(msg.to_string())
+ }
+}
+
+// Parse our own error message that looks like "{} at line {} column {}" to work
+// around erased-serde round-tripping the error through de::Error::custom.
+fn make_error(mut msg: String) -> Error {
+ let (line, column) = parse_line_col(&mut msg).unwrap_or((0, 0));
+ Error {
+ err: Box::new(ErrorImpl {
+ code: ErrorCode::Message(msg.into_boxed_str()),
+ line,
+ column,
+ }),
+ }
+}
+
+fn parse_line_col(msg: &mut String) -> Option<(usize, usize)> {
+ let start_of_suffix = match msg.rfind(" at line ") {
+ Some(index) => index,
+ None => return None,
+ };
+
+ // Find start and end of line number.
+ let start_of_line = start_of_suffix + " at line ".len();
+ let mut end_of_line = start_of_line;
+ while starts_with_digit(&msg[end_of_line..]) {
+ end_of_line += 1;
+ }
+
+ if !msg[end_of_line..].starts_with(" column ") {
+ return None;
+ }
+
+ // Find start and end of column number.
+ let start_of_column = end_of_line + " column ".len();
+ let mut end_of_column = start_of_column;
+ while starts_with_digit(&msg[end_of_column..]) {
+ end_of_column += 1;
+ }
+
+ if end_of_column < msg.len() {
+ return None;
+ }
+
+ // Parse numbers.
+ let line = match usize::from_str(&msg[start_of_line..end_of_line]) {
+ Ok(line) => line,
+ Err(_) => return None,
+ };
+ let column = match usize::from_str(&msg[start_of_column..end_of_column]) {
+ Ok(column) => column,
+ Err(_) => return None,
+ };
+
+ msg.truncate(start_of_suffix);
+ Some((line, column))
+}
+
+fn starts_with_digit(slice: &str) -> bool {
+ match slice.as_bytes().first() {
+ None => false,
+ Some(&byte) => byte >= b'0' && byte <= b'9',
+ }
+}