1use super::Client;
6use super::sign_v4::HTTPVerb;
7use super::utils::{get_request_header, into_request_failed_error, parse_xml_response};
8use crate::oss::Error;
9use bon::Builder;
10use serde::{Deserialize, Serialize};
11use serde_with::{DisplayFromStr, serde_as};
12use std::collections::HashMap;
13use url::Url;
14
15#[serde_with::skip_serializing_none]
17#[derive(Builder, Serialize)]
18#[serde(rename_all = "kebab-case")]
19pub struct PutBucket<'a> {
20 #[builder(start_fn)]
21 #[serde(skip_serializing)]
22 pub(crate) client: &'a Client,
23 #[serde(skip_serializing)]
24 pub(crate) bucket_name: &'a str,
25 #[serde(skip_serializing)]
27 pub(crate) storage_class: Option<&'a str>,
28 #[serde(skip_serializing)]
29 pub(crate) data_redundancy_type: Option<&'a str>,
30 x_oss_acl: Option<&'a str>,
32 x_oss_resource_group_id: Option<&'a str>,
33 x_oss_bucket_tagging: Option<&'a str>,
34}
35
36#[serde_with::skip_serializing_none]
37#[derive(Serialize)]
38#[serde(rename_all = "PascalCase")]
39struct CreateBucketConfiguration<'a> {
40 storage_class: Option<&'a str>,
42 data_redundancy_type: Option<&'a str>,
44}
45
46impl PutBucket<'_> {
47 pub async fn send(&self) -> Result<(), Error> {
48 let client = self.client;
49 let request_url =
50 Url::parse(&format!("https://{}.{}", self.bucket_name, client.endpoint)).unwrap();
51 let mut req_header_map: HashMap<String, String> =
52 serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap();
53
54 let creds = client.credentials_provider.load().await?;
55 if let Some(token) = &creds.sts_security_token {
56 req_header_map.insert("x-oss-security-token".to_string(), token.clone());
57 }
58
59 let header = get_request_header(
60 &creds.access_key_id,
61 &creds.access_key_secret,
62 req_header_map,
63 &request_url,
64 HTTPVerb::Put,
65 &client.region,
66 Some(self.bucket_name),
67 );
68
69 let req_xml = {
70 let create_conf = CreateBucketConfiguration {
71 storage_class: self.storage_class,
72 data_redundancy_type: self.data_redundancy_type,
73 };
74
75 quick_xml::se::to_string(&create_conf).unwrap()
76 };
77
78 let resp = client
79 .http_client
80 .put(request_url)
81 .headers(header)
82 .body(req_xml)
83 .send()
84 .await?;
85
86 if !resp.status().is_success() {
87 return Err(into_request_failed_error(resp).await);
88 }
89
90 Ok(())
91 }
92}
93#[serde_as]
98#[serde_with::skip_serializing_none]
99#[derive(Builder, Serialize)]
100#[serde(rename_all = "kebab-case")]
101pub struct ListObjectsV2<'a> {
102 #[builder(start_fn)]
103 #[serde(skip_serializing)]
104 pub(crate) client: &'a Client,
105 delimiter: Option<&'a str>,
107 start_after: Option<&'a str>,
108 continuation_token: Option<&'a str>,
109 #[serde_as(as = "Option<DisplayFromStr>")]
110 max_keys: Option<u16>,
111 prefix: Option<&'a str>,
112 encoding_type: Option<&'a str>,
113 #[serde_as(as = "Option<DisplayFromStr>")]
114 fetch_owner: Option<bool>,
115}
116
117#[derive(Deserialize, Debug)]
118#[serde(rename_all = "PascalCase")]
119pub struct ListBucketResult {
120 pub contents: Option<Vec<Content>>,
121 pub common_prefixes: Option<CommonPrefixes>,
122 pub delimiter: String,
123 pub encoding_type: Option<String>,
124 pub is_truncated: bool,
125 pub start_after: Option<String>,
126 pub max_keys: u16,
127 pub name: String,
128 pub prefix: String,
129 pub continuation_token: Option<u32>,
130 pub key_count: u32,
131 pub next_continuation_token: Option<String>,
132}
133
134#[derive(Deserialize, Debug)]
135#[serde(rename_all = "PascalCase")]
136pub struct CommonPrefixes {
137 pub prefix: String,
138}
139
140#[derive(Deserialize, Debug)]
141#[serde(rename_all = "PascalCase")]
142pub struct Content {
143 pub owner: Option<Owner>,
144 pub e_tag: String,
145 pub key: String,
146 pub last_modified: String,
147 pub size: u32,
148 pub storage_class: String,
149 pub restore_info: Option<String>,
150 pub r#type: String,
151}
152
153#[derive(Deserialize, Debug)]
154#[serde(rename_all = "PascalCase")]
155pub struct Owner {
156 pub display_name: String,
157 #[serde(rename = "ID")]
158 pub id: String,
159}
160
161impl ListObjectsV2<'_> {
162 pub async fn send(&self) -> Result<ListBucketResult, Error> {
163 let mut query_map: HashMap<String, String> =
164 serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap();
165 query_map.insert("list-type".to_owned(), "2".to_owned());
167
168 let client = self.client;
169 let sign_url = Url::parse_with_params(
170 &format!("https://{}.{}/", client.bucket, client.endpoint),
171 query_map,
172 )
173 .unwrap();
174
175 let creds = client.credentials_provider.load().await?;
176 let mut req_header_map = HashMap::new();
177 if let Some(token) = &creds.sts_security_token {
178 req_header_map.insert("x-oss-security-token".to_string(), token.clone());
179 }
180
181 let header = get_request_header(
182 &creds.access_key_id,
183 &creds.access_key_secret,
184 req_header_map,
185 &sign_url,
186 HTTPVerb::Get,
187 &client.region,
188 Some(&client.bucket),
189 );
190
191 let resp = client
192 .http_client
193 .get(sign_url)
194 .headers(header)
195 .send()
196 .await?;
197
198 let res = parse_xml_response(resp).await?;
199 Ok(res)
200 }
201}
202#[derive(Builder)]
206pub struct GetBucketInfo<'a> {
207 #[builder(start_fn)]
208 pub(crate) client: &'a Client,
209 pub(crate) bucket: &'a str,
210}
211
212#[derive(Deserialize, Debug)]
213#[serde(rename_all = "PascalCase")]
214pub struct BucketInfo {
215 pub bucket: Bucket,
216}
217
218#[derive(Deserialize, Debug)]
219#[serde(rename_all = "PascalCase")]
220pub struct Bucket {
221 pub creation_date: String,
222 pub extranet_endpoint: String,
223 pub intranet_endpoint: String,
224 pub location: String,
225 pub storage_class: String,
226 pub name: String,
227 pub resource_group_id: String,
228 pub owner: Owner,
229 pub access_control_list: AccessControlList,
230 pub data_redundancy_type: String,
231 pub versioning: Option<String>,
232 pub cross_region_replication: String,
233 pub transfer_acceleration: String,
234 pub access_monitor: String,
235 pub bucket_policy: BucketPolicy,
236 pub comment: String,
237 pub server_side_encryption_rule: ServerSideEncryptionRule,
238 pub block_public_access: bool,
239}
240
241#[derive(Deserialize, Debug)]
242pub struct ServerSideEncryptionRule {
243 #[serde(rename = "SSEAlgorithm")]
244 pub sse_algorithm: String,
245 pub kms_master_key_id: Option<String>,
246 pub kms_data_encryption: Option<String>,
247}
248
249#[derive(Deserialize, Debug)]
250#[serde(rename_all = "PascalCase")]
251pub struct AccessControlList {
252 pub grant: String,
253}
254
255#[derive(Deserialize, Debug)]
256#[serde(rename_all = "PascalCase")]
257pub struct BucketPolicy {
258 pub log_bucket: String,
259 pub log_prefix: String,
260}
261
262impl GetBucketInfo<'_> {
263 pub async fn send(&self) -> Result<BucketInfo, Error> {
264 let client = self.client;
265 let request_url = Url::parse_with_params(
266 &format!("https://{}.{}", self.bucket, client.endpoint),
267 [("bucketInfo", "")],
268 )
269 .unwrap();
270
271 let creds = client.credentials_provider.load().await?;
272 let mut req_header_map = HashMap::new();
273 if let Some(token) = &creds.sts_security_token {
274 req_header_map.insert("x-oss-security-token".to_string(), token.clone());
275 }
276
277 let header_map = get_request_header(
278 &creds.access_key_id,
279 &creds.access_key_secret,
280 req_header_map,
281 &request_url,
282 HTTPVerb::Get,
283 &client.region,
284 Some(&client.bucket),
285 );
286 let resp = client
287 .http_client
288 .get(request_url)
289 .headers(header_map)
290 .send()
291 .await?;
292
293 let res = parse_xml_response(resp).await?;
294 Ok(res)
295 }
296}
297#[derive(Builder)]
301pub struct GetBucketLocation<'a> {
302 #[builder(start_fn)]
303 pub(crate) client: &'a Client,
304 pub(crate) bucket: &'a str,
305}
306#[derive(Deserialize)]
309struct LocationConstraint {
310 #[serde(rename = "$text")]
311 field: String,
312}
313
314impl GetBucketLocation<'_> {
315 pub async fn send(&self) -> Result<String, Error> {
316 let client = self.client;
317
318 let request_url = Url::parse_with_params(
319 &format!("https://{}.{}", self.bucket, client.endpoint),
320 [("location", "")],
321 )
322 .unwrap();
323
324 let creds = client.credentials_provider.load().await?;
325 let mut req_header_map = HashMap::new();
326 if let Some(token) = &creds.sts_security_token {
327 req_header_map.insert("x-oss-security-token".to_string(), token.clone());
328 }
329
330 let header_map = get_request_header(
331 &creds.access_key_id,
332 &creds.access_key_secret,
333 req_header_map,
334 &request_url,
335 HTTPVerb::Get,
336 &client.region,
337 Some(&client.bucket),
338 );
339 let resp = client
340 .http_client
341 .get(request_url)
342 .headers(header_map)
343 .send()
344 .await?;
345
346 let res = parse_xml_response::<LocationConstraint>(resp).await?;
347 Ok(res.field)
348 }
349}
350#[derive(Builder)]
354pub struct GetBucketStat<'a> {
355 #[builder(start_fn)]
356 pub(crate) client: &'a Client,
357 pub(crate) bucket: &'a str,
358}
359
360#[derive(Deserialize, Debug)]
361#[serde(rename_all = "PascalCase")]
362pub struct BucketStat {
363 pub storage: u64,
364 pub object_count: u32,
365 pub multipart_upload_count: u32,
366 pub delete_marker_count: u32,
367 pub live_channel_count: u32,
368 pub last_modified_time: u64,
369 pub standard_storage: u64,
370 pub standard_object_count: u32,
371 pub infrequent_access_storage: u64,
372 pub infrequent_access_real_storage: u64,
373 pub infrequent_access_object_count: u32,
374 pub archive_storage: u64,
375 pub archive_real_storage: u64,
376 pub archive_object_count: u32,
377 pub cold_archive_storage: u64,
378 pub cold_archive_real_storage: u64,
379 pub cold_archive_object_count: u32,
380 pub deep_cold_archive_storage: u64,
381 pub deep_cold_archive_real_storage: u64,
382 pub deep_cold_archive_object_count: u32,
383}
384
385impl GetBucketStat<'_> {
386 pub async fn send(&self) -> Result<BucketStat, Error> {
387 let client = self.client;
388 let request_url = Url::parse_with_params(
389 &format!("https://{}.{}", self.bucket, client.endpoint),
390 [("stat", "")],
391 )
392 .unwrap();
393
394 let creds = client.credentials_provider.load().await?;
395 let mut req_header_map = HashMap::new();
396 if let Some(token) = &creds.sts_security_token {
397 req_header_map.insert("x-oss-security-token".to_string(), token.clone());
398 }
399
400 let header_map = get_request_header(
401 &creds.access_key_id,
402 &creds.access_key_secret,
403 req_header_map,
404 &request_url,
405 HTTPVerb::Get,
406 &client.region,
407 Some(&client.bucket),
408 );
409
410 let resp = client
411 .http_client
412 .get(request_url)
413 .headers(header_map)
414 .send()
415 .await?;
416
417 let res = parse_xml_response(resp).await?;
418 Ok(res)
419 }
420}
421impl Client {
424 pub fn put_bucket(&self) -> PutBucketBuilder<'_> {
425 PutBucket::builder(self)
426 }
427
428 pub fn list_objects_v2(&self) -> ListObjectsV2Builder<'_> {
429 ListObjectsV2::builder(self)
430 }
431
432 pub fn get_bucket_info(&self) -> GetBucketInfoBuilder<'_> {
433 GetBucketInfo::builder(self)
434 }
435
436 pub fn get_bucket_location(&self) -> GetBucketLocationBuilder<'_> {
437 GetBucketLocation::builder(self)
438 }
439
440 pub fn get_bucket_stat(&self) -> GetBucketStatBuilder<'_> {
441 GetBucketStat::builder(self)
442 }
443}