xt_oss/oss/
api.rs

1use super::{
2    http::{self, HeaderMap, StatusCode, Url},
3    Bytes, Response,
4};
5use base64::{engine::general_purpose, Engine as _};
6use serde::{Deserialize, Serialize};
7use std::fmt;
8
9#[derive(Debug, Serialize, Deserialize, Default, Clone)]
10pub struct ErrorMessage {
11    #[serde(rename(deserialize = "Code"))]
12    pub code: String,
13    #[serde(rename(deserialize = "Message"))]
14    pub message: String,
15    #[serde(rename(deserialize = "RequestId"))]
16    pub request_id: String,
17    #[serde(rename(deserialize = "HostId"))]
18    pub host_id: String,
19    #[serde(rename(deserialize = "EC"))]
20    pub ec: Option<String>,
21    #[serde(rename(deserialize = "RecommendDoc"))]
22    pub recommend_doc: Option<String>,
23    #[serde(rename = "OSSAccessKeyId")]
24    pub oss_access_key_id: Option<String>,
25    #[serde(rename = "SignatureProvided")]
26    pub signature_provided: Option<String>,
27    #[serde(rename = "StringToSign")]
28    pub string_to_sign: Option<String>,
29    #[serde(rename = "StringToSignBytes")]
30    pub string_to_sign_bytes: Option<String>,
31}
32
33impl fmt::Display for ErrorMessage {
34    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35        write!(f, "[{}]: {}", self.code, self.message)
36    }
37}
38
39#[derive(Debug, Clone)]
40// api 返回数据, 可能正确也可能错误
41pub struct ApiData<T> {
42    pub(crate) url: Url,
43    pub(crate) status: StatusCode,
44    pub(crate) headers: HeaderMap,
45    pub(crate) content: T,
46}
47
48impl<T> ApiData<T> {
49    pub fn url(&self) -> &Url {
50        &self.url
51    }
52
53    pub fn status(&self) -> &StatusCode {
54        &self.status
55    }
56
57    pub fn headers(&self) -> &HeaderMap {
58        &self.headers
59    }
60
61    pub fn content(self) -> T {
62        self.content
63    }
64
65    pub fn request_id(&self) -> String {
66        self.headers
67            .get("x-oss-request-id")
68            .unwrap()
69            .to_str()
70            .unwrap()
71            .into()
72    }
73
74    pub fn content_length(&self) -> Option<u64> {
75        self.headers()
76            .get(http::header::CONTENT_LENGTH)
77            .and_then(|value| value.to_str().ok())
78            .and_then(|value| value.parse().ok())
79    }
80}
81
82
83pub type ApiResponse<T> = Result<ApiData<T>, ApiData<ErrorMessage>>;
84// api 返回体, 包含请求错误, 和api返回数据
85pub type ApiResult<T = ()> = Result<ApiResponse<T>, reqwest::Error>;
86
87pub(crate) struct ApiResponseFrom(reqwest::Response);
88
89impl ApiResponseFrom {
90    pub(crate) async fn fail_message(resp: Response) -> ApiData<ErrorMessage> {
91        assert!(!resp.status().is_success());
92        let url = resp.url().clone();
93        let status = resp.status().clone();
94        let headers = resp.headers().clone();
95        let info = match resp.headers().contains_key("x-oss-err") {
96            true => {
97                let info = resp.headers().get("x-oss-err").unwrap();
98                general_purpose::STANDARD.decode(info).unwrap().to_vec()
99            }
100            false => resp.bytes().await.unwrap().to_vec(),
101        };
102        let content = String::from_utf8_lossy(&info);
103        let content = if content.is_empty() {
104            ErrorMessage::default()
105        } else {
106            quick_xml::de::from_str(&content).unwrap()
107        };
108        ApiData {
109            url,
110            status,
111            headers,
112            content,
113        }
114    }
115
116    pub(crate) async fn bytes_data(resp: Response) -> ApiData<Bytes> {
117        assert!(resp.status().is_success());
118        let url = resp.url().clone();
119        let status = resp.status().clone();
120        let headers = resp.headers().clone();
121        let content = resp.bytes().await.unwrap();
122        ApiData {
123            url,
124            status,
125            headers,
126            content,
127        }
128    }
129
130    pub(crate) async fn to_type<T>(self) -> ApiResponse<T>
131    where
132        T: for<'a> Deserialize<'a>,
133    {
134        let resp = self.0;
135        if resp.status().is_success() {
136            let url = resp.url().clone();
137            let status = resp.status().clone();
138            let headers = resp.headers().clone();
139            let content = resp.bytes().await.unwrap();
140            let content = String::from_utf8_lossy(&content);
141            // dbg!(&content);
142            let content: T = quick_xml::de::from_str(&content).unwrap();
143
144            Ok(ApiData {
145                url,
146                status,
147                headers,
148                content,
149            })
150        } else {
151            let data_fail_message = Self::fail_message(resp).await;
152            Err(data_fail_message)
153        }
154    }
155
156    pub(crate) async fn to_bytes(self) -> ApiResponse<Bytes> {
157        let resp = self.0;
158        if resp.status().is_success() {
159            let data = Self::bytes_data(resp).await;
160            Ok(data)
161        } else {
162            let data_fail_message = Self::fail_message(resp).await;
163            Err(data_fail_message)
164        }
165    }
166
167    pub(crate) async fn to_text(self) -> ApiResponse<String> {
168        let resp = self.0;
169        if resp.status().is_success() {
170            let url = resp.url().clone();
171            let status = resp.status().clone();
172            let headers = resp.headers().clone();
173            let content = resp.text().await.unwrap();
174            Ok(ApiData {
175                url,
176                status,
177                headers,
178                content,
179            })
180        } else {
181            let data_fail_message = Self::fail_message(resp).await;
182            Err(data_fail_message)
183        }
184    }
185
186    pub(crate) async fn to_empty(self) -> ApiResponse<()> {
187        let resp = self.0;
188        if resp.status().is_success() {
189            Ok(ApiData {
190                url: resp.url().clone(),
191                status: resp.status().clone(),
192                headers: resp.headers().clone(),
193                content: (),
194            })
195        } else {
196            let data_fail_message = Self::fail_message(resp).await;
197            Err(data_fail_message)
198        }
199    }
200}
201
202fn insert_header<T: ToString + std::fmt::Display>(
203    headers: &mut http::HeaderMap,
204    key: http::header::HeaderName,
205    value: T,
206) {
207    headers.insert(
208        key,
209        value
210            .to_string()
211            .parse()
212            .expect("Failed to parse header value"),
213    );
214}
215
216fn insert_custom_header<T: ToString + std::fmt::Display>(
217    headers: &mut http::HeaderMap,
218    key: &str,
219    value: T,
220) {
221    let header_name =
222        http::HeaderName::from_bytes(key.as_bytes()).expect("Failed to create header name");
223    headers.insert(
224        header_name,
225        value
226            .to_string()
227            .parse()
228            .expect("Failed to parse header value"),
229    );
230}
231
232pub(crate) mod bucket;
233pub(crate) mod objects;
234pub(crate) mod region;
235pub(crate) mod service;