azure_core_mirror/error/
http_error.rs1use crate::{headers, Response, StatusCode};
2use bytes::Bytes;
3use std::collections::HashMap;
4
5#[derive(Debug)]
7pub struct HttpError {
8 status: StatusCode,
9 details: ErrorDetails,
10 headers: std::collections::HashMap<String, String>,
11 body: Bytes,
12}
13
14impl HttpError {
15 pub async fn new(response: Response) -> Self {
19 let status = response.status();
20 let headers: HashMap<String, String> = response
21 .headers()
22 .iter()
23 .map(|(name, value)| (name.as_str().to_owned(), value.as_str().to_owned()))
24 .collect();
25 let body = response
26 .into_body()
27 .collect()
28 .await
29 .unwrap_or_else(|_| Bytes::from_static(b"<ERROR COLLECTING BODY>"));
30 let details = ErrorDetails::new(&headers, &body);
31 HttpError {
32 status,
33 details,
34 headers,
35 body,
36 }
37 }
38
39 pub fn status(&self) -> StatusCode {
41 self.status
42 }
43
44 pub fn error_code(&self) -> Option<&str> {
46 self.details.code.as_deref()
47 }
48
49 pub fn error_message(&self) -> Option<&str> {
51 self.details.message.as_deref()
52 }
53}
54
55impl std::fmt::Display for HttpError {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 let newline = if f.alternate() { "\n" } else { " " };
58 let tab = if f.alternate() { "\t" } else { " " };
59 write!(f, "HttpError {{{newline}")?;
60 write!(f, "{tab}Status: {},{newline}", self.status)?;
61 write!(
62 f,
63 "{tab}Error Code: {},{newline}",
64 self.details
65 .code
66 .as_deref()
67 .unwrap_or("<unknown error code>")
68 )?;
69 write!(f, "{tab}Body: \"{:?}\",{newline}", self.body)?;
71 write!(f, "{tab}Headers: [{newline}")?;
72 for (k, v) in &self.headers {
74 write!(f, "{tab}{tab}{}:{}{newline}", k, v)?;
75 }
76 write!(f, "{tab}],{newline}}}{newline}")?;
77
78 Ok(())
79 }
80}
81
82impl std::error::Error for HttpError {}
83
84#[derive(Debug)]
85struct ErrorDetails {
86 code: Option<String>,
87 message: Option<String>,
88}
89
90impl ErrorDetails {
91 fn new(headers: &HashMap<String, String>, body: &[u8]) -> Self {
92 let mut code = get_error_code_from_header(headers);
93 code = code.or_else(|| get_error_code_from_body(body));
94 let message = get_error_message_from_body(body);
95 Self { code, message }
96 }
97}
98
99fn get_error_code_from_header(headers: &HashMap<String, String>) -> Option<String> {
103 headers.get(headers::ERROR_CODE.as_str()).cloned()
104}
105
106pub(crate) fn get_error_code_from_body(body: &[u8]) -> Option<String> {
110 let json = serde_json::from_slice::<serde_json::Value>(body).ok()?;
111 let nested = || json.get("error")?.get("code")?.as_str();
112 let top_level = || json.get("code")?.as_str();
113 let code = nested().or_else(top_level);
114 code.map(|c| c.to_owned())
115}
116
117pub(crate) fn get_error_message_from_body(body: &[u8]) -> Option<String> {
121 let json = serde_json::from_slice::<serde_json::Value>(body).ok()?;
122 let nested = || json.get("error")?.get("message")?.as_str();
123 let top_level = || json.get("message")?.as_str();
124 let code = nested().or_else(top_level);
125 code.map(|c| c.to_owned())
126}