aws_smithy_json/deserialize/
error.rs
1use 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 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 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 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}