1use crate::oss;
2
3use self::builders::{
4 DeleteBucketBuilder, GetBucketInfoBuilder, GetBucketLocationBuilder, GetBucketStatBuilder,
5 ListObjectBuilder, ListObjectsV2Builder, PutBucketBuilder,
6};
7
8pub mod builders {
9 use crate::oss::{
10 self,
11 api::{self, insert_custom_header, ApiResponseFrom},
12 entities::{
13 bucket::{
14 BucketInfo, BucketStat, CreateBucketConfiguration, ListBucketResult,
15 ListBucketResult2, LocationConstraint,
16 },
17 DataRedundancyType, OssAcl, StorageClass,
18 },
19 http,
20 };
21 use reqwest::header::HeaderMap;
22 use serde::{Deserialize, Serialize};
23 use std::fmt;
24
25 #[derive(Debug)]
26 pub struct PutBucketBuilder<'a> {
27 client: &'a oss::Client<'a>,
28 region: Option<&'a str>,
29 bucket: Option<&'a str>,
30 acl: Option<OssAcl>,
31 group_id: Option<&'a str>,
32 storage_class: Option<StorageClass>,
33 data_redundancy_type: Option<DataRedundancyType>,
34 }
35
36 impl<'a> PutBucketBuilder<'a> {
37 pub(crate) fn new(client: &'a oss::Client) -> Self {
38 Self {
39 client,
40 region: None,
41 bucket: None,
42 acl: None,
43 group_id: None,
44 storage_class: None,
46 data_redundancy_type: None,
47 }
48 }
49
50 pub fn with_region(mut self, value: &'a str) -> Self {
51 self.region = Some(value);
52 self
53 }
54
55 pub fn with_bucket(mut self, value: &'a str) -> Self {
56 self.bucket = Some(value);
57 self
58 }
59
60 pub fn with_acl(mut self, value: OssAcl) -> Self {
61 self.acl = Some(value);
62 self
63 }
64
65 pub fn with_group_id(mut self, value: &'a str) -> Self {
66 self.group_id = Some(value);
67 self
68 }
69
70 pub fn with_storage_class(mut self, value: StorageClass) -> Self {
71 self.storage_class = Some(value);
72 self
73 }
74
75 pub fn with_data_redundancy_type(mut self, value: DataRedundancyType) -> Self {
76 self.data_redundancy_type = Some(value);
77 self
78 }
79
80 fn headers(&self) -> HeaderMap {
81 let mut headers = HeaderMap::default();
82 if let Some(acl) = &self.acl {
83 insert_custom_header(&mut headers, "x-oss-acl", acl.to_string());
84 }
85 if let Some(group_id) = &self.group_id {
86 insert_custom_header(&mut headers, "x-oss-resource-group-id", group_id);
87 }
88 headers
89 }
90
91 fn config(&self) -> String {
92 let config = CreateBucketConfiguration {
93 storage_class: self.storage_class.to_owned(),
94 data_redundancy_type: self.data_redundancy_type.to_owned(),
95 };
96 quick_xml::se::to_string(&config).unwrap()
97 }
98
99 pub async fn execute(&self) -> api::ApiResult {
101 let region = self.region.unwrap_or(self.client.options.region);
102 let bucket = self.bucket.unwrap_or(self.client.bucket());
103 let res = format!("/{}/", bucket);
104 let url = format!(
105 "{}://{}.{}",
106 self.client.options.schema(),
107 bucket,
108 format!(
109 "{}{}.{}",
110 region,
111 match self.client.options.internal {
112 true => "-internal",
113 false => "",
114 },
115 oss::BASE_URL
116 )
117 );
118
119 let headers = self.headers();
120 let config = oss::Bytes::from(self.config());
121
122 let resp = self
123 .client
124 .request
125 .task()
126 .with_url(&url)
127 .with_method(http::Method::PUT)
128 .with_resource(&res)
129 .with_headers(headers)
130 .with_body(config)
131 .execute()
132 .await?;
133
134 Ok(ApiResponseFrom(resp).to_empty().await)
135 }
136 }
137
138 pub struct DeleteBucketBuilder<'a> {
139 client: &'a oss::Client<'a>,
140 region: Option<&'a str>,
141 bucket: Option<&'a str>,
142 }
143
144 impl<'a> DeleteBucketBuilder<'a> {
145 pub fn new(client: &'a oss::Client) -> Self {
146 Self {
147 client,
148 region: None,
149 bucket: None,
150 }
151 }
152
153 pub fn with_region(mut self, region: &'a str) -> Self {
154 self.region = Some(region);
155 self
156 }
157
158 pub fn with_bucket(mut self, bucket: &'a str) -> Self {
159 self.bucket = Some(bucket);
160 self
161 }
162
163 pub async fn execute(&self) -> api::ApiResult {
164 let region = self.region.unwrap_or(self.client.options.region);
165 let bucket = self.bucket.unwrap_or(self.client.bucket());
166 let res = format!("/{}/", bucket);
167 let url = format!(
168 "{}://{}.{}",
169 self.client.options.schema(),
170 bucket,
171 format!(
172 "{}{}.{}",
173 region,
174 match self.client.options.internal {
175 true => "-internal",
176 false => "",
177 },
178 oss::BASE_URL
179 )
180 );
181 let resp = self
185 .client
186 .request
187 .task()
188 .with_url(&url)
189 .with_resource(&res)
190 .with_method(http::Method::DELETE)
191 .execute()
192 .await?;
193
194 Ok(ApiResponseFrom(resp).to_empty().await)
195 }
196 }
197
198 #[derive(Debug, Serialize, Deserialize)]
199 pub(crate) struct ListObjectQuery<'a> {
200 delimiter: Option<&'a str>,
201 marker: Option<&'a str>,
202 #[serde(rename = "max-keys")]
203 max_keys: Option<i32>,
204 prefix: Option<&'a str>,
205 #[serde(rename = "encoding-type")]
206 encoding_type: Option<&'a str>,
207 }
208
209 impl<'a> fmt::Display for ListObjectQuery<'a> {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(f, "{}", serde_qs::to_string(self).unwrap())
212 }
213 }
214
215 impl<'a> Default for ListObjectQuery<'a> {
216 fn default() -> Self {
217 ListObjectQuery {
218 delimiter: None,
219 marker: None,
220 max_keys: Some(100),
221 prefix: None,
222 encoding_type: None,
223 }
224 }
225 }
226
227 pub struct ListObjectBuilder<'a> {
228 client: &'a oss::Client<'a>,
229 query: ListObjectQuery<'a>,
230 }
231
232 impl<'a> ListObjectBuilder<'a> {
233 pub fn new(client: &'a oss::Client) -> Self {
234 Self {
235 client,
236 query: ListObjectQuery::default(),
237 }
238 }
239
240 pub fn with_delimiter(mut self, value: &'a str) -> Self {
241 self.query.delimiter = Some(value);
242 self
243 }
244
245 pub fn with_marker(mut self, value: &'a str) -> Self {
246 self.query.marker = Some(value);
247 self
248 }
249
250 pub fn with_max_keys(mut self, value: i32) -> Self {
251 self.query.max_keys = Some(value);
252 self
253 }
254
255 pub fn with_prefix(mut self, value: &'a str) -> Self {
256 self.query.prefix = Some(value);
257 self
258 }
259
260 pub fn with_encoding_type(mut self, value: &'a str) -> Self {
261 self.query.encoding_type = Some(value);
262 self
263 }
264
265 pub async fn execute(&self) -> api::ApiResult<ListBucketResult> {
266 let res = format!("/{}/", self.client.bucket());
267 let mut url = self.client.base_url();
268 let query = self.query.to_string();
269 if !query.is_empty() {
270 url = format!("{}?{}", url, query);
271 }
272
273 let resp = self
274 .client
275 .request
276 .task()
277 .with_url(&url)
278 .with_resource(&res)
279 .execute()
280 .await?;
281
282 Ok(ApiResponseFrom(resp).to_type().await)
283 }
284 }
285
286 #[derive(Debug, Serialize, Deserialize)]
287 pub(crate) struct ListObjectsV2Query<'a> {
288 #[serde(rename = "list-type")]
289 pub list_type: u8,
290 pub delimiter: Option<&'a str>,
291 #[serde(rename = "start-after")]
292 pub start_after: Option<&'a str>,
293 #[serde(rename = "continuation-token")]
294 pub continuation_token: Option<&'a str>,
295 #[serde(rename = "max-keys")]
296 pub max_keys: Option<i32>,
297 pub prefix: Option<&'a str>,
298 #[serde(rename = "encoding-type")]
299 pub encoding_type: Option<&'a str>,
300 #[serde(rename = "fetch-owner")]
301 pub fetch_owner: Option<bool>,
302 }
303
304 impl<'a> Default for ListObjectsV2Query<'a> {
305 fn default() -> Self {
306 ListObjectsV2Query {
307 list_type: 2,
308 delimiter: None,
309 start_after: None,
310 continuation_token: None,
311 max_keys: Some(100),
312 prefix: None,
313 encoding_type: None,
314 fetch_owner: None,
315 }
316 }
317 }
318
319 impl<'a> fmt::Display for ListObjectsV2Query<'a> {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 write!(f, "{}", serde_qs::to_string(self).unwrap())
322 }
323 }
324
325 pub struct ListObjectsV2Builder<'a> {
326 client: &'a oss::Client<'a>,
327 query: ListObjectsV2Query<'a>,
328 }
329
330 impl<'a> ListObjectsV2Builder<'a> {
331 pub fn new(client: &'a oss::Client) -> Self {
332 Self {
333 client,
334 query: ListObjectsV2Query::default(),
335 }
336 }
337
338 pub fn with_delimiter(mut self, value: &'a str) -> Self {
339 self.query.delimiter = Some(value);
340 self
341 }
342
343 pub fn with_start_after(mut self, value: &'a str) -> Self {
344 self.query.delimiter = Some(value);
345 self
346 }
347
348 pub fn with_continuation_token(mut self, value: Option<&'a str>) -> Self {
349 self.query.continuation_token = value;
350 self
351 }
352
353 pub fn with_max_keys(mut self, value: i32) -> Self {
354 self.query.max_keys = Some(value);
355 self
356 }
357
358 pub fn with_prefix(mut self, value: &'a str) -> Self {
359 self.query.prefix = Some(value);
360 self
361 }
362
363 pub fn with_encoding_type(mut self, value: &'a str) -> Self {
364 self.query.encoding_type = Some(value);
365 self
366 }
367
368 pub fn with_fetch_owner(mut self, value: bool) -> Self {
369 self.query.fetch_owner = Some(value);
370 self
371 }
372
373 pub async fn execute(&self) -> api::ApiResult<ListBucketResult2> {
374 let mut res = format!("/{}/", self.client.bucket());
375 let mut url = self.client.base_url();
376 let query = self.query.to_string();
377 if !query.is_empty() {
378 if let Some(token) = self.query.continuation_token {
379 res = format!("{}?continuation-token={}", res, token);
380 }
381 url = format!("{}?{}", url, query);
382 }
383
384 let resp = self
385 .client
386 .request
387 .task()
388 .with_url(&url)
389 .with_method(http::Method::GET)
390 .with_resource(&res)
391 .execute()
392 .await?;
393
394 Ok(ApiResponseFrom(resp).to_type().await)
395 }
396 }
397
398 pub struct GetBucketInfoBuilder<'a> {
399 client: &'a oss::Client<'a>,
400 bucket: Option<&'a str>,
401 }
402
403 impl<'a> GetBucketInfoBuilder<'a> {
404 pub fn new(client: &'a oss::Client) -> Self {
405 Self {
406 client,
407 bucket: None,
408 }
409 }
410
411 pub fn with_bucket(mut self, value: &'a str) -> Self {
412 self.bucket = Some(value);
413 self
414 }
415
416 pub async fn execute(&self) -> api::ApiResult<BucketInfo> {
417 let region = self.client.region();
418 let bucket = self.bucket.unwrap_or(self.client.bucket());
419 let res = format!("/{}/?bucketInfo", bucket);
420 let url = format!(
421 "{}://{}.{}?bucketInfo",
422 self.client.options.schema(),
423 bucket,
424 format!(
425 "{}{}.{}",
426 region,
427 match self.client.options.internal {
428 true => "-internal",
429 false => "",
430 },
431 oss::BASE_URL
432 )
433 );
434
435 let resp = self
436 .client
437 .request
438 .task()
439 .with_url(&url)
440 .with_resource(&res)
441 .execute()
442 .await?;
443
444 Ok(ApiResponseFrom(resp).to_type().await)
445 }
446 }
447
448 pub struct GetBucketLocationBuilder<'a> {
449 client: &'a oss::Client<'a>,
450 bucket: Option<&'a str>,
451 }
452
453 impl<'a> GetBucketLocationBuilder<'a> {
454 pub fn new(client: &'a oss::Client) -> Self {
455 Self {
456 client,
457 bucket: None,
458 }
459 }
460
461 pub fn with_bucket(mut self, value: &'a str) -> Self {
462 self.bucket = Some(value);
463 self
464 }
465
466 pub async fn execute(&self) -> api::ApiResult<LocationConstraint> {
467 let region = self.client.options.region;
468 let bucket = self.bucket.unwrap_or(self.client.bucket());
469 let res = format!("/{}/?location", bucket);
470 let url = format!(
471 "{}://{}.{}/?location",
472 self.client.options.schema(),
473 bucket,
474 format!(
475 "{}{}.{}",
476 region,
477 match self.client.options.internal {
478 true => "-internal",
479 false => "",
480 },
481 oss::BASE_URL
482 )
483 );
484 let resp = self
485 .client
486 .request
487 .task()
488 .with_url(&url)
489 .with_resource(&res)
490 .with_method(http::Method::GET)
491 .execute()
492 .await?;
493
494 Ok(ApiResponseFrom(resp).to_type().await)
495 }
496 }
497
498 pub struct GetBucketStatBuilder<'a> {
499 client: &'a oss::Client<'a>,
500 region: Option<&'a str>,
501 bucket: Option<&'a str>,
502 }
503
504 impl<'a> GetBucketStatBuilder<'a> {
505 pub fn new(client: &'a oss::Client) -> Self {
506 Self {
507 client,
508 region: None,
509 bucket: None,
510 }
511 }
512
513 pub fn with_region(mut self, region: &'a str) -> Self {
514 self.region = Some(region);
515 self
516 }
517
518 pub fn with_bucket(mut self, bucket: &'a str) -> Self {
519 self.bucket = Some(bucket);
520 self
521 }
522
523 pub async fn execute(&self) -> api::ApiResult<BucketStat> {
524 let region = self.region.unwrap_or(self.client.options.region);
525 let bucket = self.bucket.unwrap_or(self.client.bucket());
526 let res = format!("/{}/?stat", bucket);
527 let url = format!(
528 "{}://{}.{}/?stat",
529 self.client.options.schema(),
530 bucket,
531 format!(
532 "{}{}.{}",
533 region,
534 match self.client.options.internal {
535 true => "-internal",
536 false => "",
537 },
538 oss::BASE_URL
539 )
540 );
541
542 let resp = self
543 .client
544 .request
545 .task()
546 .with_url(&url)
547 .with_resource(&res)
548 .with_method(http::Method::GET)
549 .execute()
550 .await?;
551
552 Ok(ApiResponseFrom(resp).to_type().await)
553 }
554 }
555}
556
557#[allow(non_snake_case)]
559impl<'a> oss::Client<'a> {
560 pub fn PutBucket(&self) -> PutBucketBuilder<'_> {
565 PutBucketBuilder::new(self)
566 }
567
568 pub fn DeleteBucket(&self) -> DeleteBucketBuilder<'_> {
573 DeleteBucketBuilder::new(self)
574 }
575
576 pub fn ListObjects(&self) -> ListObjectBuilder<'_> {
582 ListObjectBuilder::new(self)
583 }
584
585 pub fn ListObjectsV2(&self) -> ListObjectsV2Builder<'_> {
591 ListObjectsV2Builder::new(self)
592 }
593
594 pub fn GetBucketInfo(&self) -> GetBucketInfoBuilder<'_> {
599 GetBucketInfoBuilder::new(self)
600 }
601
602 pub fn GetBucketLocation(&self) -> GetBucketLocationBuilder<'_> {
608 GetBucketLocationBuilder::new(self)
609 }
610
611 pub fn GetBucketStat(&self) -> GetBucketStatBuilder<'_> {
617 GetBucketStatBuilder::new(self)
618 }
619}