1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

use crate::escape::EscapeError;
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt;
use std::str::Utf8Error;

#[derive(Debug)]
pub(in crate::deserialize) enum DeserializeErrorKind {
    Custom {
        message: Cow<'static, str>,
        source: Option<Box<dyn StdError + Send + Sync + 'static>>,
    },
    ExpectedLiteral(String),
    InvalidEscape(char),
    InvalidNumber,
    InvalidUtf8,
    UnescapeFailed(EscapeError),
    UnexpectedControlCharacter(u8),
    UnexpectedEos,
    UnexpectedToken(char, &'static str),
}

#[derive(Debug)]
pub struct DeserializeError {
    pub(in crate::deserialize) kind: DeserializeErrorKind,
    pub(in crate::deserialize) offset: Option<usize>,
}

impl DeserializeError {
    pub(in crate::deserialize) fn new(kind: DeserializeErrorKind, offset: Option<usize>) -> Self {
        Self { kind, offset }
    }

    /// Returns a custom error without an offset.
    pub fn custom(message: impl Into<Cow<'static, str>>) -> Self {
        Self::new(
            DeserializeErrorKind::Custom {
                message: message.into(),
                source: None,
            },
            None,
        )
    }

    /// Returns a custom error with an error source without an offset.
    pub fn custom_source(
        message: impl Into<Cow<'static, str>>,
        source: impl Into<Box<dyn StdError + Send + Sync + 'static>>,
    ) -> Self {
        Self::new(
            DeserializeErrorKind::Custom {
                message: message.into(),
                source: Some(source.into()),
            },
            None,
        )
    }

    /// Adds an offset to the error.
    pub fn with_offset(mut self, offset: usize) -> Self {
        self.offset = Some(offset);
        self
    }
}

impl StdError for DeserializeError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use DeserializeErrorKind::*;
        match &self.kind {
            UnescapeFailed(source) => Some(source),
            Custom {
                source: Some(source),
                ..
            } => Some(source.as_ref()),
            Custom { source: None, .. }
            | ExpectedLiteral(_)
            | InvalidEscape(_)
            | InvalidNumber
            | InvalidUtf8
            | UnexpectedControlCharacter(_)
            | UnexpectedToken(..)
            | UnexpectedEos => None,
        }
    }
}

impl fmt::Display for DeserializeError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        use DeserializeErrorKind::*;
        if let Some(offset) = self.offset {
            write!(f, "Error at offset {}: ", offset)?;
        }
        match &self.kind {
            Custom { message, .. } => write!(f, "failed to parse JSON: {message}"),
            ExpectedLiteral(literal) => write!(f, "expected literal: {literal}"),
            InvalidEscape(escape) => write!(f, "invalid JSON escape: \\{escape}"),
            InvalidNumber => write!(f, "invalid number"),
            InvalidUtf8 => write!(f, "invalid UTF-8 codepoint in JSON stream"),
            UnescapeFailed(_) => write!(f, "failed to unescape JSON string"),
            UnexpectedControlCharacter(value) => write!(
                f,
                "encountered unescaped control character in string: 0x{value:X}"
            ),
            UnexpectedToken(token, expected) => {
                write!(f, "unexpected token '{token}'. Expected one of {expected}",)
            }
            UnexpectedEos => write!(f, "unexpected end of stream"),
        }
    }
}

impl From<Utf8Error> for DeserializeErrorKind {
    fn from(_: Utf8Error) -> Self {
        DeserializeErrorKind::InvalidUtf8
    }
}

impl From<EscapeError> for DeserializeError {
    fn from(err: EscapeError) -> Self {
        Self {
            kind: DeserializeErrorKind::UnescapeFailed(err),
            offset: None,
        }
    }
}

impl From<aws_smithy_types::error::TryFromNumberError> for DeserializeError {
    fn from(_: aws_smithy_types::error::TryFromNumberError) -> Self {
        Self {
            kind: DeserializeErrorKind::InvalidNumber,
            offset: None,
        }
    }
}