1use serde::{Deserialize, Serialize};
2use std::fmt::{Display, Formatter, Result as FmtResult};
3
4#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
5#[non_exhaustive]
6#[serde(untagged)]
7pub enum ApiError {
8 General(GeneralApiError),
9 Ratelimited(RatelimitedApiError),
11 Message(MessageApiError),
13}
14
15impl Display for ApiError {
16 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
17 match self {
18 Self::General(inner) => Display::fmt(inner, f),
19 Self::Message(inner) => Display::fmt(inner, f),
20 Self::Ratelimited(inner) => Display::fmt(inner, f),
21 }
22 }
23}
24
25#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
26#[non_exhaustive]
27pub struct GeneralApiError {
28 pub code: u64,
29 pub message: String,
30}
31
32impl Display for GeneralApiError {
33 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
34 f.write_str("Error code ")?;
35 Display::fmt(&self.code, f)?;
36 f.write_str(": ")?;
37
38 f.write_str(&self.message)
39 }
40}
41
42#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
45#[non_exhaustive]
46pub struct MessageApiError {
47 pub embed: Option<Vec<MessageApiErrorEmbedField>>,
49}
50
51impl Display for MessageApiError {
52 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
53 f.write_str("message fields invalid: ")?;
54
55 if let Some(embed) = &self.embed {
56 f.write_str("embed (")?;
57
58 let field_count = embed.len().saturating_sub(1);
59
60 for (idx, field) in embed.iter().enumerate() {
61 Display::fmt(field, f)?;
62
63 if idx == field_count {
64 f.write_str(", ")?;
65 }
66 }
67
68 f.write_str(")")?;
69 }
70
71 Ok(())
72 }
73}
74
75#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
79#[non_exhaustive]
80#[serde(rename_all = "snake_case")]
81pub enum MessageApiErrorEmbedField {
82 Fields,
84 Timestamp,
86}
87
88impl Display for MessageApiErrorEmbedField {
89 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
90 f.write_str(match self {
91 Self::Fields => "fields",
92 Self::Timestamp => "timestamp",
93 })
94 }
95}
96
97#[derive(Clone, Debug, Deserialize, Serialize)]
98#[non_exhaustive]
99pub struct RatelimitedApiError {
100 pub global: bool,
102 pub message: String,
104 pub retry_after: f64,
106}
107
108impl Display for RatelimitedApiError {
109 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
110 f.write_str("Got ")?;
111
112 if self.global {
113 f.write_str("global ")?;
114 }
115
116 f.write_str("ratelimited for ")?;
117 Display::fmt(&self.retry_after, f)?;
118
119 f.write_str("s")
120 }
121}
122
123impl Eq for RatelimitedApiError {}
124
125impl PartialEq for RatelimitedApiError {
126 fn eq(&self, other: &Self) -> bool {
127 self.global == other.global && self.message == other.message
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::{
134 ApiError, GeneralApiError, MessageApiError, MessageApiErrorEmbedField, RatelimitedApiError,
135 };
136 use serde_test::Token;
137
138 #[test]
139 fn api_error_deser() {
140 let expected = GeneralApiError {
141 code: 10001,
142 message: "Unknown account".to_owned(),
143 };
144
145 serde_test::assert_tokens(
146 &expected,
147 &[
148 Token::Struct {
149 name: "GeneralApiError",
150 len: 2,
151 },
152 Token::Str("code"),
153 Token::U64(10001),
154 Token::Str("message"),
155 Token::Str("Unknown account"),
156 Token::StructEnd,
157 ],
158 );
159 }
160
161 #[test]
162 fn api_error_message() {
163 let expected = ApiError::Message(MessageApiError {
164 embed: Some(
165 [
166 MessageApiErrorEmbedField::Fields,
167 MessageApiErrorEmbedField::Timestamp,
168 ]
169 .to_vec(),
170 ),
171 });
172
173 serde_test::assert_tokens(
174 &expected,
175 &[
176 Token::Struct {
177 name: "MessageApiError",
178 len: 1,
179 },
180 Token::Str("embed"),
181 Token::Some,
182 Token::Seq { len: Some(2) },
183 Token::UnitVariant {
184 name: "MessageApiErrorEmbedField",
185 variant: "fields",
186 },
187 Token::UnitVariant {
188 name: "MessageApiErrorEmbedField",
189 variant: "timestamp",
190 },
191 Token::SeqEnd,
192 Token::StructEnd,
193 ],
194 );
195 }
196
197 #[test]
198 fn ratelimited_api_error() {
199 let expected = RatelimitedApiError {
200 global: true,
201 message: "You are being rate limited.".to_owned(),
202 retry_after: 6.457,
203 };
204
205 serde_test::assert_tokens(
206 &expected,
207 &[
208 Token::Struct {
209 name: "RatelimitedApiError",
210 len: 3,
211 },
212 Token::Str("global"),
213 Token::Bool(true),
214 Token::Str("message"),
215 Token::Str("You are being rate limited."),
216 Token::Str("retry_after"),
217 Token::F64(6.457),
218 Token::StructEnd,
219 ],
220 );
221 }
222
223 #[test]
231 fn api_error_variant_ratelimited() {
232 let expected = ApiError::Ratelimited(RatelimitedApiError {
233 global: false,
234 message: "You are being rate limited.".to_owned(),
235 retry_after: 0.362,
236 });
237
238 serde_test::assert_tokens(
239 &expected,
240 &[
241 Token::Struct {
242 name: "RatelimitedApiError",
243 len: 3,
244 },
245 Token::Str("global"),
246 Token::Bool(false),
247 Token::Str("message"),
248 Token::Str("You are being rate limited."),
249 Token::Str("retry_after"),
250 Token::F64(0.362),
251 Token::StructEnd,
252 ],
253 );
254 }
255}