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)]
40pub 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>>;
84pub 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 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;