mountpoint_s3_client/
error_metadata.rs

1use std::os::unix::ffi::OsStrExt;
2
3use mountpoint_s3_crt::s3::client::MetaRequestResult;
4
5/// Additional data fetched from S3 response, which caused an error
6#[derive(Default, Debug, Clone, PartialEq, Eq)]
7pub struct ClientErrorMetadata {
8    /// http code of the response, e.g. 403
9    pub http_code: Option<i32>,
10    /// error code from the response xml body, e.g. "AccessDenied"
11    pub error_code: Option<String>,
12    /// error message from the response xml body, e.g. "Access Denied"
13    pub error_message: Option<String>,
14}
15
16/// Allows using metadata of errors in generic implementations without knowing the exact type of an error,
17/// which as of today may be s3_crt_client::S3RequestError / mock_client::MockClientError / GetObjectError and etc.
18pub trait ProvideErrorMetadata {
19    fn meta(&self) -> ClientErrorMetadata;
20}
21
22impl ClientErrorMetadata {
23    pub fn from_meta_request_result(result: &MetaRequestResult) -> Self {
24        let http_code = if result.response_status >= 100 {
25            Some(result.response_status)
26        } else {
27            None
28        };
29
30        let no_body_result = Self {
31            http_code,
32            ..Default::default()
33        };
34        let Some(body) = result.error_response_body.as_ref() else {
35            return no_body_result;
36        };
37        let Some(root) = xmltree::Element::parse(body.as_bytes()).ok() else {
38            return no_body_result;
39        };
40
41        let error_code = root
42            .get_child("Code")
43            .and_then(|raw| raw.get_text())
44            .map(|field_str| field_str.to_string());
45        let error_message = root
46            .get_child("Message")
47            .and_then(|raw| raw.get_text())
48            .map(|field_str| field_str.to_string());
49
50        Self {
51            http_code,
52            error_code,
53            error_message,
54        }
55    }
56}