use serde::Deserialize;
use crate::apis::Error;
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct ErrorDetails {
pub status_code: u16,
pub error_code: String,
#[serde(default)]
pub message: Option<ErrorMessage>,
#[serde(default)]
pub response_id: Option<String>
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum ErrorMessage {
One(String),
Many(Vec<String>)
}
impl ErrorDetails {
#[must_use]
pub fn from_api_error<E>(error: &Error<E>) -> Option<Self> {
match error {
Error::ResponseError(response) => serde_json::from_str(&response.content).ok(),
Error::Reqwest(_) | Error::Serde(_) | Error::Io(_) => None
}
}
#[must_use]
pub fn messages(&self) -> Vec<String> {
match &self.message {
None => Vec::new(),
Some(ErrorMessage::One(message)) => vec![message.clone()],
Some(ErrorMessage::Many(messages)) => messages.clone()
}
}
}
#[cfg(test)]
mod tests {
use super::{ErrorDetails, ErrorMessage};
use crate::apis::{Error, ResponseContent};
fn response_error(content: &str) -> Error<()> {
Error::ResponseError(ResponseContent {
status: reqwest::StatusCode::BAD_REQUEST,
content: content.to_string(),
entity: None
})
}
#[test]
fn parses_a_single_message_envelope() {
let error = response_error(
r#"{
"status_code": 400,
"error_code": "bad_request",
"message": "invalid preset",
"response_id": "3037b284-a6ac-4dc7-b6d7-1f624dcfcec6"
}"#
);
let details = ErrorDetails::from_api_error(&error).expect("envelope parses");
assert_eq!(details.status_code, 400);
assert_eq!(details.error_code, "bad_request");
assert_eq!(details.messages(), vec!["invalid preset".to_string()]);
assert_eq!(
details.response_id.as_deref(),
Some("3037b284-a6ac-4dc7-b6d7-1f624dcfcec6")
);
}
#[test]
fn parses_a_message_list_envelope() {
let error = response_error(
r#"{"status_code": 400, "error_code": "bad_request", "message": ["a", "b"]}"#
);
let details = ErrorDetails::from_api_error(&error).expect("envelope parses");
assert_eq!(
details.message,
Some(ErrorMessage::Many(vec!["a".to_string(), "b".to_string()]))
);
assert_eq!(details.messages(), vec!["a".to_string(), "b".to_string()]);
}
#[test]
fn tolerates_a_missing_message() {
let error = response_error(r#"{"status_code": 500, "error_code": "internal"}"#);
let details = ErrorDetails::from_api_error(&error).expect("envelope parses");
assert!(details.message.is_none());
assert!(details.messages().is_empty());
}
#[test]
fn returns_none_for_non_envelope_bodies_and_other_errors() {
assert!(ErrorDetails::from_api_error(&response_error("<html>oops</html>")).is_none());
let serde_error: Error<()> =
Error::Serde(serde_json::from_str::<u32>("x").expect_err("must fail"));
assert!(ErrorDetails::from_api_error(&serde_error).is_none());
}
}