hotshot_query_service/explorer/
errors.rs1use std::fmt::{Debug, Display};
14
15use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
16use tide_disco::StatusCode;
17
18pub trait ExplorerAPIError: Display + Debug {
24 fn code(&self) -> &str;
28}
29
30impl std::error::Error for dyn ExplorerAPIError {
31 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32 None
33 }
34
35 fn description(&self) -> &str {
36 "description() is deprecated; use Display"
37 }
38
39 fn cause(&self) -> Option<&dyn std::error::Error> {
40 self.source()
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
48#[serde(tag = "code", rename = "UNIMPLEMENTED")]
49pub struct Unimplemented {}
50
51impl Unimplemented {
52 pub fn status(&self) -> StatusCode {
53 StatusCode::NOT_IMPLEMENTED
54 }
55}
56
57impl ExplorerAPIError for Unimplemented {
58 fn code(&self) -> &str {
59 "UNIMPLEMENTED"
60 }
61}
62
63impl Display for Unimplemented {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "this feature is not yet implemented")
67 }
68}
69
70impl std::error::Error for Unimplemented {}
71
72impl Serialize for Unimplemented {
73 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
74 where
75 S: Serializer,
76 {
77 let mut st = serializer.serialize_struct("Unimplemented", 2)?;
78 st.serialize_field("code", &self.code())?;
79 st.serialize_field("message", &format!("{self}"))?;
80 st.end()
81 }
82}
83
84#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
87#[serde(tag = "code", rename = "INVALID_LIMIT")]
88pub struct InvalidLimit {}
89
90impl InvalidLimit {
91 pub fn status(&self) -> StatusCode {
92 StatusCode::BAD_REQUEST
93 }
94}
95
96impl ExplorerAPIError for InvalidLimit {
97 fn code(&self) -> &str {
98 "INVALID_LIMIT"
99 }
100}
101
102impl Display for InvalidLimit {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 write!(
105 f,
106 "limit must be provided, and must be a positive integer less than 100"
107 )
108 }
109}
110
111impl std::error::Error for InvalidLimit {}
112
113impl Serialize for InvalidLimit {
114 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
115 where
116 S: Serializer,
117 {
118 let mut st = serializer.serialize_struct("InvalidLimit", 2)?;
119 st.serialize_field("code", &self.code())?;
120 st.serialize_field("message", &format!("{self}"))?;
121 st.end()
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
128#[serde(tag = "code", rename = "NOT_FOUND")]
129pub struct NotFound {
130 pub key: String,
131}
132
133impl NotFound {
134 pub fn status(&self) -> StatusCode {
135 StatusCode::NOT_FOUND
136 }
137
138 pub fn key(&self) -> &str {
139 &self.key
140 }
141}
142
143impl ExplorerAPIError for NotFound {
144 fn code(&self) -> &str {
145 "NOT_FOUND"
146 }
147}
148
149impl Display for NotFound {
150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151 write!(f, "no data found for key: '{}'", self.key)
152 }
153}
154
155impl std::error::Error for NotFound {}
156
157impl Serialize for NotFound {
158 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
159 where
160 S: Serializer,
161 {
162 let mut st = serializer.serialize_struct("NotFound", 2)?;
163 st.serialize_field("code", &self.code())?;
164 st.serialize_field("key", &self.key())?;
165 st.serialize_field("message", &format!("{self}"))?;
166 st.end()
167 }
168}
169
170#[derive(Debug, Clone, Deserialize)]
173#[serde(tag = "code", rename = "QUERY_ERROR")]
174pub struct QueryError {
175 pub error: crate::QueryError,
176}
177
178impl QueryError {
179 pub fn status(&self) -> StatusCode {
180 self.error.status()
181 }
182
183 pub fn error(&self) -> &crate::QueryError {
184 &self.error
185 }
186}
187
188impl ExplorerAPIError for QueryError {
189 fn code(&self) -> &str {
190 "QUERY_ERROR"
191 }
192}
193
194impl Display for QueryError {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 write!(
197 f,
198 "encountered error attempting to retrieve data: '{}'",
199 self.error
200 )
201 }
202}
203
204impl std::error::Error for QueryError {
205 fn cause(&self) -> Option<&dyn std::error::Error> {
206 Some(&self.error)
207 }
208}
209
210impl Serialize for QueryError {
211 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
212 where
213 S: Serializer,
214 {
215 let mut st = serializer.serialize_struct("QueryError", 2)?;
216 st.serialize_field("code", &self.code())?;
217 st.serialize_field("error", &self.error())?;
218 st.serialize_field("message", &format!("{self}"))?;
219 st.end()
220 }
221}
222
223impl From<crate::QueryError> for QueryError {
224 fn from(error: crate::QueryError) -> Self {
225 Self { error }
226 }
227}
228
229#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
232#[serde(tag = "code", rename = "BAD_QUERY")]
233pub struct BadQuery {}
234
235impl BadQuery {
236 pub fn status(&self) -> StatusCode {
237 StatusCode::BAD_REQUEST
238 }
239}
240
241impl ExplorerAPIError for BadQuery {
242 fn code(&self) -> &str {
243 "BAD_QUERY"
244 }
245}
246
247impl Display for BadQuery {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 write!(f, "provided query is invalid, or malformed")
250 }
251}
252
253impl std::error::Error for BadQuery {}
254
255impl Serialize for BadQuery {
256 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
257 where
258 S: Serializer,
259 {
260 let mut st = serializer.serialize_struct("BadQuery", 2)?;
261 st.serialize_field("code", &self.code())?;
262 st.serialize_field("message", &format!("{self}"))?;
263 st.end()
264 }
265}
266
267#[cfg(test)]
268mod test {
269 use super::{BadQuery, InvalidLimit, NotFound, QueryError, Unimplemented};
270
271 #[test]
272 fn test_serialize_deserialize_unimplemented() {
273 let unimplemented = Unimplemented {};
274 let serialize_result = serde_json::to_string(&unimplemented);
275 assert!(
276 serialize_result.is_ok(),
277 "failed to serialize Unimplemented: {}",
278 serialize_result.err().unwrap(),
279 );
280 let serialized = serialize_result.unwrap();
281
282 {
283 let have = &serialized;
284 let want =
285 r#"{"code":"UNIMPLEMENTED","message":"this feature is not yet implemented"}"#;
286 assert_eq!(
287 have, want,
288 "serialized Unimplemented mismatch: have: {have}, want: {want}"
289 );
290 }
291
292 let deserialize_result: Result<Unimplemented, _> = serde_json::from_str(&serialized);
293 assert!(
294 deserialize_result.is_ok(),
295 "failed to deserialize Unimplemented: {}",
296 deserialize_result.err().unwrap(),
297 );
298 let deserialized = deserialize_result.unwrap();
299 {
300 let have = deserialized;
301 let want = unimplemented;
302 assert_eq!(
303 have, want,
304 "deserialized Unimplemented mismatch: have: {have}, want: {want}"
305 );
306 }
307 }
308
309 #[test]
310 fn test_serialize_deserialize_invalid_limit() {
311 let invalid_limit = InvalidLimit {};
312 let serialize_result = serde_json::to_string(&invalid_limit);
313 assert!(
314 serialize_result.is_ok(),
315 "failed to serialize InvalidLimit: {}",
316 serialize_result.err().unwrap(),
317 );
318 let serialized = serialize_result.unwrap();
319
320 {
321 let have = &serialized;
322 let want = r#"{"code":"INVALID_LIMIT","message":"limit must be provided, and must be a positive integer less than 100"}"#;
323 assert_eq!(
324 have, want,
325 "serialized InvalidLimit mismatch: have: {have}, want: {want}"
326 );
327 }
328
329 let deserialize_result: Result<InvalidLimit, _> = serde_json::from_str(&serialized);
330 assert!(
331 deserialize_result.is_ok(),
332 "failed to deserialize InvalidLimit: {}",
333 deserialize_result.err().unwrap(),
334 );
335 let deserialized = deserialize_result.unwrap();
336 {
337 let have = deserialized;
338 let want = invalid_limit;
339 assert_eq!(
340 have, want,
341 "deserialized InvalidLimit mismatch: have: {have}, want: {want}"
342 );
343 }
344 }
345
346 #[test]
347 fn test_serialize_deserialize_not_found() {
348 let not_found = NotFound {
349 key: "foo".to_string(),
350 };
351 let serialize_result = serde_json::to_string(¬_found);
352 assert!(
353 serialize_result.is_ok(),
354 "failed to serialize NotFound: {}",
355 serialize_result.err().unwrap(),
356 );
357 let serialized = serialize_result.unwrap();
358
359 {
360 let have = &serialized;
361 let want =
362 r#"{"code":"NOT_FOUND","key":"foo","message":"no data found for key: 'foo'"}"#;
363 assert_eq!(
364 have, want,
365 "serialized NotFound mismatch: have: {have}, want: {want}"
366 );
367 }
368
369 let deserialize_result: Result<NotFound, _> = serde_json::from_str(&serialized);
370 assert!(
371 deserialize_result.is_ok(),
372 "failed to deserialize NotFound: {}",
373 deserialize_result.err().unwrap(),
374 );
375 let deserialized = deserialize_result.unwrap();
376 {
377 let have = deserialized;
378 let want = not_found;
379 assert_eq!(
380 have, want,
381 "deserialized NotFound mismatch: have: {have}, want: {want}"
382 );
383 }
384 }
385
386 #[test]
387 fn test_serialize_deserialize_query_error() {
388 let query_error = QueryError {
389 error: crate::QueryError::NotFound,
390 };
391 let serialize_result = serde_json::to_string(&query_error);
392 assert!(
393 serialize_result.is_ok(),
394 "failed to serialize QueryError: {}",
395 serialize_result.err().unwrap(),
396 );
397 let serialized = serialize_result.unwrap();
398
399 {
400 let have = &serialized;
401 let want = r#"{"code":"QUERY_ERROR","error":"NotFound","message":"encountered error attempting to retrieve data: 'The requested resource does not exist or is not known to this query service.'"}"#;
402 assert_eq!(
403 have, want,
404 "serialized QueryError mismatch: have: {have}, want: {want}"
405 );
406 }
407
408 let deserialize_result: Result<QueryError, _> = serde_json::from_str(&serialized);
409 assert!(
410 deserialize_result.is_ok(),
411 "failed to deserialize QueryError: {}",
412 deserialize_result.err().unwrap(),
413 );
414 let deserialized = deserialize_result.unwrap();
415 {
416 let have = deserialized;
417 let want = query_error;
418
419 match &have.error {
420 crate::QueryError::NotFound => {},
421 _ => panic!("deserialized QueryError mismatch: have: {have}, want: {want}"),
422 }
423 }
424 }
425
426 #[test]
427 fn test_serialize_deserialize_bad_query() {
428 let bad_query = BadQuery {};
429 let serialize_result = serde_json::to_string(&bad_query);
430 assert!(
431 serialize_result.is_ok(),
432 "failed to serialize BadQuery: {}",
433 serialize_result.err().unwrap(),
434 );
435 let serialized = serialize_result.unwrap();
436
437 {
438 let have = &serialized;
439 let want =
440 r#"{"code":"BAD_QUERY","message":"provided query is invalid, or malformed"}"#;
441 assert_eq!(
442 have, want,
443 "serialized BadQuery mismatch: have: {have}, want: {want}"
444 );
445 }
446
447 let deserialize_result: Result<BadQuery, _> = serde_json::from_str(&serialized);
448 assert!(
449 deserialize_result.is_ok(),
450 "failed to deserialize BadQuery: {}",
451 deserialize_result.err().unwrap(),
452 );
453 let deserialized = deserialize_result.unwrap();
454 {
455 let have = deserialized;
456 let want = bad_query;
457
458 assert_eq!(
459 have, want,
460 "deserialized BadQuery mismatch: have: {have}, want: {want}"
461 );
462 }
463 }
464}