azure_storage_blob/models/
error.rs1use crate::generated::models::StorageErrorCode;
5use azure_core::{error::ErrorKind, http::RawResponse, xml};
6use serde::Deserialize;
7use serde_json::Value;
8use std::collections::HashMap;
9
10pub type Result<T> = std::result::Result<T, StorageError>;
12
13#[derive(Debug, Clone)]
15pub struct StorageError {
16 pub status_code: azure_core::http::StatusCode,
18 pub error_code: Option<StorageErrorCode>,
20 pub message: Option<String>,
22 pub request_id: Option<String>,
24 pub reason: Option<String>,
26 pub authentication_error_detail: Option<String>,
28 pub copy_source_status_code: Option<azure_core::http::StatusCode>,
30 pub copy_source_error_code: Option<String>,
32 pub copy_source_error_message: Option<String>,
34 pub additional_error_info: HashMap<String, String>,
36}
37
38impl StorageError {
39 fn value_to_string(value: &Value) -> String {
41 match value {
42 Value::Null => "null".to_string(),
44 Value::Bool(b) => b.to_string(),
45 Value::Number(n) => n.to_string(),
46 Value::String(s) => s.clone(),
47
48 Value::Object(obj) if obj.len() == 1 && obj.contains_key("$text") => obj
50 .get("$text")
51 .and_then(|v| v.as_str())
52 .map(String::from)
53 .unwrap_or_default(),
54
55 _ => serde_json::to_string(value).unwrap_or_default(),
57 }
58 }
59
60 fn from_xml(
62 status_code: azure_core::http::StatusCode,
63 error_code: Option<StorageErrorCode>,
64 request_id: Option<String>,
65 raw_response: RawResponse,
66 ) -> std::result::Result<Self, azure_core::Error> {
67 #[derive(Deserialize)]
68 #[serde(rename = "Error")]
69 struct StorageErrorXml {
70 #[serde(rename = "Code")]
71 code: Option<String>,
72 #[serde(rename = "Message")]
73 message: Option<String>,
74 #[serde(rename = "Reason")]
75 reason: Option<String>,
76 #[serde(rename = "AuthenticationErrorDetail")]
77 authentication_error_detail: Option<String>,
78 #[serde(rename = "CopySourceStatusCode")]
79 copy_source_status_code: Option<String>,
80 #[serde(rename = "CopySourceErrorCode")]
81 copy_source_error_code: Option<String>,
82 #[serde(rename = "CopySourceErrorMessage")]
83 copy_source_error_message: Option<String>,
84 #[serde(flatten)]
85 additional_fields: HashMap<String, Value>,
86 }
87
88 let xml_fields = xml::from_xml::<_, StorageErrorXml>(raw_response.body())?;
89
90 let additional_error_info = xml_fields
92 .additional_fields
93 .iter()
94 .map(|(k, v)| (k.clone(), Self::value_to_string(v)))
95 .collect();
96
97 Ok(StorageError {
98 status_code,
99 error_code,
100 message: xml_fields.message,
101 request_id,
102 reason: xml_fields.reason,
103 authentication_error_detail: xml_fields.authentication_error_detail,
104 copy_source_status_code: xml_fields
105 .copy_source_status_code
106 .and_then(|s| s.parse::<u16>().ok())
107 .map(azure_core::http::StatusCode::from),
108 copy_source_error_code: xml_fields.copy_source_error_code,
109 copy_source_error_message: xml_fields.copy_source_error_message,
110 additional_error_info,
111 })
112 }
113}
114
115impl std::fmt::Display for StorageError {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 writeln!(f, "HTTP Status Code: {}", self.status_code)?;
118
119 if let Some(request_id) = &self.request_id {
120 writeln!(f, "Request ID: {}", request_id)?;
121 }
122
123 if let Some(error_code) = &self.error_code {
124 writeln!(f, "Storage Error Code: {}", error_code)?;
125 }
126
127 if let Some(message) = &self.message {
128 writeln!(f, "Error Message: {}", message)?;
129 }
130
131 if let Some(reason) = &self.reason {
132 writeln!(f, "Reason: {}", reason)?;
133 }
134
135 if let Some(detail) = &self.authentication_error_detail {
136 writeln!(f, "Authentication Error Detail: {}", detail)?;
137 }
138
139 if let Some(status) = &self.copy_source_status_code {
140 writeln!(f, "Copy Source Status Code: {}", status)?;
141 }
142
143 if let Some(code) = &self.copy_source_error_code {
144 writeln!(f, "Copy Source Error Code: {}", code)?;
145 }
146
147 if let Some(message) = &self.copy_source_error_message {
148 writeln!(f, "Copy Source Error Message: {}", message)?;
149 }
150
151 if !self.additional_error_info.is_empty() {
152 writeln!(f, "\nAdditional Error Info:")?;
153 for (key, value) in &self.additional_error_info {
154 writeln!(f, "{}: {}", key, value)?;
155 }
156 }
157
158 Ok(())
159 }
160}
161
162impl std::error::Error for StorageError {}
163
164impl TryFrom<azure_core::Error> for StorageError {
165 type Error = azure_core::Error;
166
167 fn try_from(error: azure_core::Error) -> std::result::Result<Self, Self::Error> {
168 match error.kind() {
169 ErrorKind::HttpResponse {
170 status,
171 raw_response: Some(raw_response),
172 ..
173 } => {
174 let headers = raw_response.headers();
175 let body = raw_response.body();
176
177 let error_code = headers
178 .get_optional_string(&azure_core::http::headers::HeaderName::from_static(
179 "x-ms-error-code",
180 ))
181 .and_then(|code| {
182 code.parse()
183 .ok()
184 .or(Some(StorageErrorCode::UnknownValue(code)))
185 });
186
187 let request_id = headers.get_optional_string(
188 &azure_core::http::headers::HeaderName::from_static("x-ms-request-id"),
189 );
190
191 if body.is_empty() {
192 let message = Some(status.canonical_reason().to_string());
194
195 return Ok(StorageError {
196 status_code: *status,
197 error_code,
198 message,
199 request_id,
200 reason: None,
201 authentication_error_detail: None,
202 copy_source_status_code: None,
203 copy_source_error_code: None,
204 copy_source_error_message: None,
205 additional_error_info: HashMap::new(),
206 });
207 }
208
209 StorageError::from_xml(
210 *status,
211 error_code,
212 request_id,
213 raw_response.as_ref().clone(),
214 )
215 }
216 ErrorKind::HttpResponse {
218 status,
219 raw_response: None,
220 ..
221 } => {
222 let message = Some(status.canonical_reason().to_string());
223 Ok(StorageError {
224 status_code: *status,
225 error_code: None,
226 message,
227 request_id: None,
228 reason: None,
229 authentication_error_detail: None,
230 copy_source_status_code: None,
231 copy_source_error_code: None,
232 copy_source_error_message: None,
233 additional_error_info: HashMap::new(),
234 })
235 }
236 _ => Err(azure_core::Error::with_message(
237 azure_core::error::ErrorKind::DataConversion,
238 "ErrorKind was not HttpResponse and could not be parsed.",
239 )),
240 }
241 }
242}