1use serde::Deserialize;
4
5use crate::apis::Error;
6
7#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
27pub struct ErrorDetails {
28 pub status_code: u16,
30 pub error_code: String,
32 #[serde(default)]
34 pub message: Option<ErrorMessage>,
35 #[serde(default)]
37 pub response_id: Option<String>
38}
39
40#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
42#[serde(untagged)]
43pub enum ErrorMessage {
44 One(String),
46 Many(Vec<String>)
48}
49
50impl ErrorDetails {
51 #[must_use]
56 pub fn from_api_error<E>(error: &Error<E>) -> Option<Self> {
57 match error {
58 Error::ResponseError(response) => serde_json::from_str(&response.content).ok(),
59 Error::Reqwest(_) | Error::Serde(_) | Error::Io(_) => None
60 }
61 }
62
63 #[must_use]
65 pub fn messages(&self) -> Vec<String> {
66 match &self.message {
67 None => Vec::new(),
68 Some(ErrorMessage::One(message)) => vec![message.clone()],
69 Some(ErrorMessage::Many(messages)) => messages.clone()
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::{ErrorDetails, ErrorMessage};
77 use crate::apis::{Error, ResponseContent};
78
79 fn response_error(content: &str) -> Error<()> {
80 Error::ResponseError(ResponseContent {
81 status: reqwest::StatusCode::BAD_REQUEST,
82 content: content.to_string(),
83 entity: None
84 })
85 }
86
87 #[test]
88 fn parses_a_single_message_envelope() {
89 let error = response_error(
90 r#"{
91 "status_code": 400,
92 "error_code": "bad_request",
93 "message": "invalid preset",
94 "response_id": "3037b284-a6ac-4dc7-b6d7-1f624dcfcec6"
95 }"#
96 );
97 let details = ErrorDetails::from_api_error(&error).expect("envelope parses");
98 assert_eq!(details.status_code, 400);
99 assert_eq!(details.error_code, "bad_request");
100 assert_eq!(details.messages(), vec!["invalid preset".to_string()]);
101 assert_eq!(
102 details.response_id.as_deref(),
103 Some("3037b284-a6ac-4dc7-b6d7-1f624dcfcec6")
104 );
105 }
106
107 #[test]
108 fn parses_a_message_list_envelope() {
109 let error = response_error(
110 r#"{"status_code": 400, "error_code": "bad_request", "message": ["a", "b"]}"#
111 );
112 let details = ErrorDetails::from_api_error(&error).expect("envelope parses");
113 assert_eq!(
114 details.message,
115 Some(ErrorMessage::Many(vec!["a".to_string(), "b".to_string()]))
116 );
117 assert_eq!(details.messages(), vec!["a".to_string(), "b".to_string()]);
118 }
119
120 #[test]
121 fn tolerates_a_missing_message() {
122 let error = response_error(r#"{"status_code": 500, "error_code": "internal"}"#);
123 let details = ErrorDetails::from_api_error(&error).expect("envelope parses");
124 assert!(details.message.is_none());
125 assert!(details.messages().is_empty());
126 }
127
128 #[test]
129 fn returns_none_for_non_envelope_bodies_and_other_errors() {
130 assert!(ErrorDetails::from_api_error(&response_error("<html>oops</html>")).is_none());
131 let serde_error: Error<()> =
132 Error::Serde(serde_json::from_str::<u32>("x").expect_err("must fail"));
133 assert!(ErrorDetails::from_api_error(&serde_error).is_none());
134 }
135}