aws_smithy_json/deserialize/
error.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use crate::escape::EscapeError;
7use std::borrow::Cow;
8use std::error::Error as StdError;
9use std::fmt;
10use std::str::Utf8Error;
11
12#[derive(Debug)]
13pub(in crate::deserialize) enum DeserializeErrorKind {
14    Custom {
15        message: Cow<'static, str>,
16        source: Option<Box<dyn StdError + Send + Sync + 'static>>,
17    },
18    ExpectedLiteral(String),
19    InvalidEscape(char),
20    InvalidNumber,
21    InvalidUtf8,
22    UnescapeFailed(EscapeError),
23    UnexpectedControlCharacter(u8),
24    UnexpectedEos,
25    UnexpectedToken(char, &'static str),
26}
27
28#[derive(Debug)]
29pub struct DeserializeError {
30    pub(in crate::deserialize) kind: DeserializeErrorKind,
31    pub(in crate::deserialize) offset: Option<usize>,
32}
33
34impl DeserializeError {
35    pub(in crate::deserialize) fn new(kind: DeserializeErrorKind, offset: Option<usize>) -> Self {
36        Self { kind, offset }
37    }
38
39    /// Returns a custom error without an offset.
40    pub fn custom(message: impl Into<Cow<'static, str>>) -> Self {
41        Self::new(
42            DeserializeErrorKind::Custom {
43                message: message.into(),
44                source: None,
45            },
46            None,
47        )
48    }
49
50    /// Returns a custom error with an error source without an offset.
51    pub fn custom_source(
52        message: impl Into<Cow<'static, str>>,
53        source: impl Into<Box<dyn StdError + Send + Sync + 'static>>,
54    ) -> Self {
55        Self::new(
56            DeserializeErrorKind::Custom {
57                message: message.into(),
58                source: Some(source.into()),
59            },
60            None,
61        )
62    }
63
64    /// Adds an offset to the error.
65    pub fn with_offset(mut self, offset: usize) -> Self {
66        self.offset = Some(offset);
67        self
68    }
69}
70
71impl StdError for DeserializeError {
72    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
73        use DeserializeErrorKind::*;
74        match &self.kind {
75            UnescapeFailed(source) => Some(source),
76            Custom {
77                source: Some(source),
78                ..
79            } => Some(source.as_ref()),
80            Custom { source: None, .. }
81            | ExpectedLiteral(_)
82            | InvalidEscape(_)
83            | InvalidNumber
84            | InvalidUtf8
85            | UnexpectedControlCharacter(_)
86            | UnexpectedToken(..)
87            | UnexpectedEos => None,
88        }
89    }
90}
91
92impl fmt::Display for DeserializeError {
93    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94        use DeserializeErrorKind::*;
95        if let Some(offset) = self.offset {
96            write!(f, "Error at offset {}: ", offset)?;
97        }
98        match &self.kind {
99            Custom { message, .. } => write!(f, "failed to parse JSON: {message}"),
100            ExpectedLiteral(literal) => write!(f, "expected literal: {literal}"),
101            InvalidEscape(escape) => write!(f, "invalid JSON escape: \\{escape}"),
102            InvalidNumber => write!(f, "invalid number"),
103            InvalidUtf8 => write!(f, "invalid UTF-8 codepoint in JSON stream"),
104            UnescapeFailed(_) => write!(f, "failed to unescape JSON string"),
105            UnexpectedControlCharacter(value) => write!(
106                f,
107                "encountered unescaped control character in string: 0x{value:X}"
108            ),
109            UnexpectedToken(token, expected) => {
110                write!(f, "unexpected token '{token}'. Expected one of {expected}",)
111            }
112            UnexpectedEos => write!(f, "unexpected end of stream"),
113        }
114    }
115}
116
117impl From<Utf8Error> for DeserializeErrorKind {
118    fn from(_: Utf8Error) -> Self {
119        DeserializeErrorKind::InvalidUtf8
120    }
121}
122
123impl From<EscapeError> for DeserializeError {
124    fn from(err: EscapeError) -> Self {
125        Self {
126            kind: DeserializeErrorKind::UnescapeFailed(err),
127            offset: None,
128        }
129    }
130}
131
132impl From<aws_smithy_types::error::TryFromNumberError> for DeserializeError {
133    fn from(_: aws_smithy_types::error::TryFromNumberError) -> Self {
134        Self {
135            kind: DeserializeErrorKind::InvalidNumber,
136            offset: None,
137        }
138    }
139}