Skip to main content

u_sdk/oss/
service.rs

1use super::Client;
2use super::sign_v4::HTTPVerb;
3use super::utils::{get_request_header, parse_xml_response};
4use crate::oss::Error;
5use bon::Builder;
6use serde::{Deserialize, Serialize};
7use serde_with::{DisplayFromStr, serde_as};
8use std::collections::HashMap;
9use url::Url;
10
11#[serde_as]
12#[serde_with::skip_serializing_none]
13#[derive(Builder, Serialize)]
14#[serde(rename_all = "kebab-case")]
15pub struct ListBuckets<'a> {
16    #[builder(start_fn)]
17    #[serde(skip_serializing)]
18    pub(crate) client: &'a Client,
19    // 请求头
20    #[serde(skip_serializing)]
21    pub(crate) x_oss_resource_group_id: Option<&'a str>,
22    // 请求参数
23    pub(crate) prefix: Option<&'a str>,
24    pub(crate) marker: Option<&'a str>,
25    #[serde_as(as = "Option<DisplayFromStr>")]
26    pub(crate) max_keys: Option<u16>,
27}
28
29// region:    --- ListBucketResult
30/// 如果属性值为`None`,如:`prefix: None`,表示返回的xml中没有该标签`<Prefix/>`。
31#[derive(Deserialize, Debug)]
32#[serde(rename_all = "PascalCase")]
33pub struct ListAllMyBucketsResult {
34    pub prefix: Option<String>,
35    pub marker: Option<String>,
36    pub max_keys: Option<u32>,
37    pub is_truncated: Option<bool>,
38    pub next_marker: Option<String>,
39    pub owner: Owner,
40    // 原来的xml中包含了buckets->Option<Vec<Bucket>这种结构,这里使用自定义反序列化函数来减少嵌套
41    #[serde(deserialize_with = "unwrap_buckets")]
42    pub buckets: Vec<Bucket>,
43}
44
45#[derive(Deserialize, Debug)]
46#[serde(rename_all = "PascalCase")]
47struct Buckets {
48    #[serde(default)]
49    // 这里按照xml结构应该为 `bucket: Option<Vec<Bucket>>`,这里使用default来简化
50    bucket: Vec<Bucket>,
51}
52
53fn unwrap_buckets<'de, D>(deserializer: D) -> Result<Vec<Bucket>, D::Error>
54where
55    D: serde::Deserializer<'de>,
56{
57    let helper = Buckets::deserialize(deserializer)?;
58    Ok(helper.bucket)
59}
60
61#[derive(Deserialize, Debug)]
62#[serde(rename_all = "PascalCase")]
63pub struct Owner {
64    #[serde(rename = "ID")]
65    pub id: String,
66    pub display_name: String,
67}
68
69#[derive(Deserialize, Debug)]
70#[serde(rename_all = "PascalCase")]
71pub struct Bucket {
72    pub name: String,
73    pub creation_date: String,
74    pub location: String,
75    pub extranet_endpoint: String,
76    pub intranet_endpoint: String,
77    pub region: String,
78    pub storage_class: String,
79    pub resource_group_id: Option<String>,
80}
81// endregion: --- ListBucketResult
82
83impl ListBuckets<'_> {
84    pub async fn send(&self) -> Result<ListAllMyBucketsResult, Error> {
85        // 构建url的query部分,用于传递url以便签名构建canonical_query_string
86        let query_map: HashMap<String, String> =
87            serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap();
88
89        let client = self.client;
90        // 构建url用于签名使用;签名使用的url的host在构建签名的时候用不到的,理论上可以是任意值,这里用endpoint
91        // 此api不涉及bucket,url使用https://endpoint
92        let request_url =
93            Url::parse_with_params(&format!("https://{}", client.endpoint), query_map).unwrap();
94
95        let mut request_header_map = HashMap::new();
96        if let Some(s) = self.x_oss_resource_group_id {
97            request_header_map.insert("x-oss-resource-group-id".to_owned(), s.to_owned());
98        }
99
100        let creds = client.credentials_provider.load().await?;
101        if let Some(token) = &creds.sts_security_token {
102            request_header_map.insert("x-oss-security-token".to_string(), token.clone());
103        }
104
105        let header_map = get_request_header(
106            &creds.access_key_id,
107            &creds.access_key_secret,
108            request_header_map,
109            &request_url,
110            HTTPVerb::Get,
111            &client.region,
112            None,
113        );
114
115        let resp = client
116            .http_client
117            .get(request_url)
118            .headers(header_map)
119            .send()
120            .await?;
121
122        let res = parse_xml_response(resp).await?;
123        Ok(res)
124    }
125}
126
127impl Client {
128    pub fn list_buckets(&self) -> ListBucketsBuilder<'_> {
129        ListBuckets::builder(self)
130    }
131}