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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

//! Errors for operations

use aws_smithy_types::date_time::DateTimeFormatError;
use http::uri::InvalidUri;
use std::borrow::Cow;
use std::error::Error;
use std::fmt::{Display, Formatter};

#[derive(Debug)]
enum SerializationErrorKind {
    CannotSerializeUnknownVariant { union: &'static str },
    DateTimeFormatError { cause: DateTimeFormatError },
}

/// An error that occurs when serialization of an operation fails.
#[derive(Debug)]
pub struct SerializationError {
    kind: SerializationErrorKind,
}

impl SerializationError {
    /// An error that occurs when serialization of an operation fails for an unknown reason.
    pub fn unknown_variant(union: &'static str) -> Self {
        Self {
            kind: SerializationErrorKind::CannotSerializeUnknownVariant { union },
        }
    }
}

impl Display for SerializationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self.kind {
            SerializationErrorKind::CannotSerializeUnknownVariant { union } => write!(
                f,
                "Cannot serialize `{union}::Unknown`. Unknown union variants cannot be serialized. \
                This can occur when round-tripping a response from the server that was not \
                recognized by the SDK. Consider upgrading to the latest version of the SDK.",
            ),
            SerializationErrorKind::DateTimeFormatError { .. } => {
                write!(f, "failed to serialize timestamp")
            }
        }
    }
}

impl Error for SerializationError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.kind {
            SerializationErrorKind::CannotSerializeUnknownVariant { .. } => None,
            SerializationErrorKind::DateTimeFormatError { cause } => Some(cause as _),
        }
    }
}

impl From<DateTimeFormatError> for SerializationError {
    fn from(err: DateTimeFormatError) -> SerializationError {
        Self {
            kind: SerializationErrorKind::DateTimeFormatError { cause: err },
        }
    }
}

#[derive(Debug)]
enum BuildErrorKind {
    /// A field contained an invalid value
    InvalidField {
        field: &'static str,
        details: String,
    },
    /// A field was missing
    MissingField {
        field: &'static str,
        details: &'static str,
    },
    /// The serializer could not serialize the input
    SerializationError(SerializationError),

    /// The serializer did not produce a valid URI
    ///
    /// This typically indicates that a field contained invalid characters.
    InvalidUri {
        uri: String,
        message: Cow<'static, str>,
        source: InvalidUri,
    },

    /// An error occurred request construction
    Other(Box<dyn Error + Send + Sync + 'static>),
}

/// An error occurred attempting to build an `Operation` from an input
///
/// These are almost always due to user error caused by limitations of specific fields due to
/// protocol serialization (e.g. fields that can only be a subset ASCII because they are serialized
/// as the name of an HTTP header)
#[derive(Debug)]
pub struct BuildError {
    kind: BuildErrorKind,
}

impl BuildError {
    pub(crate) fn invalid_uri(uri: String, message: Cow<'static, str>, source: InvalidUri) -> Self {
        Self {
            kind: BuildErrorKind::InvalidUri {
                uri,
                message,
                source,
            },
        }
    }

    /// Construct a build error for a missing field
    pub fn missing_field(field: &'static str, details: &'static str) -> Self {
        Self {
            kind: BuildErrorKind::MissingField { field, details },
        }
    }

    /// Construct a build error for a missing field
    pub fn invalid_field(field: &'static str, details: impl Into<String>) -> Self {
        Self {
            kind: BuildErrorKind::InvalidField {
                field,
                details: details.into(),
            },
        }
    }

    /// Construct a build error from another underlying error
    pub fn other(source: impl Into<Box<dyn Error + Send + Sync + 'static>>) -> Self {
        Self {
            kind: BuildErrorKind::Other(source.into()),
        }
    }
}

impl From<SerializationError> for BuildError {
    fn from(err: SerializationError) -> Self {
        Self {
            kind: BuildErrorKind::SerializationError(err),
        }
    }
}

impl From<DateTimeFormatError> for BuildError {
    fn from(err: DateTimeFormatError) -> Self {
        Self::from(SerializationError::from(err))
    }
}

impl Display for BuildError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match &self.kind {
            BuildErrorKind::InvalidField { field, details } => {
                write!(f, "invalid field in input: {field} (details: {details})")
            }
            BuildErrorKind::MissingField { field, details } => {
                write!(f, "{field} was missing: {details}")
            }
            BuildErrorKind::SerializationError(_) => {
                write!(f, "failed to serialize input")
            }
            BuildErrorKind::InvalidUri { uri, message, .. } => {
                write!(f, "generated URI `{uri}` was not a valid URI: {message}")
            }
            BuildErrorKind::Other(_) => {
                write!(f, "error during request construction")
            }
        }
    }
}

impl Error for BuildError {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        match &self.kind {
            BuildErrorKind::SerializationError(source) => Some(source as _),
            BuildErrorKind::Other(source) => Some(source.as_ref()),
            BuildErrorKind::InvalidUri { source, .. } => Some(source as _),
            BuildErrorKind::InvalidField { .. } | BuildErrorKind::MissingField { .. } => None,
        }
    }
}