huaweicloud_sdk_rust_obs/
client.rs

1use std::{collections::HashMap, time::Duration};
2
3use reqwest::{header::HeaderMap, Body, Method, Response};
4
5use crate::{
6    auth::Authorization,
7    bucket::Bucket,
8    config::{Config, SecurityHolder, SignatureType},
9    error::ObsError,
10};
11
12#[derive(Debug)]
13pub struct Client {
14    config: Config,
15    http_client: reqwest::Client,
16}
17
18impl Client {
19    /// endpoint 格式: https[http]://obs.cn-north-4.myhuaweicloud.com
20    pub fn new<S: ToString>(
21        access_key_id: S,
22        secret_access_key: S,
23        endpoint: &str,
24    ) -> Result<Client, ObsError> {
25        ClientBuilder::new()
26            .security_provider(access_key_id, secret_access_key)
27            .endpoint(endpoint)
28            .build()
29    }
30
31    pub fn security(&self) -> Option<SecurityHolder> {
32        if !self.config().security_providers().is_empty() {
33            for sh in self.config().security_providers() {
34                if !sh.sk().is_empty() && !sh.ak().is_empty() {
35                    return Some(sh.clone());
36                }
37            }
38            None
39        } else {
40            None
41        }
42    }
43    pub fn bucket<'a>(&'a self, name: &'a str) -> Bucket {
44        Bucket::new(name, self)
45    }
46
47    pub fn builder() -> ClientBuilder {
48        ClientBuilder::new()
49    }
50
51    pub fn config(&self) -> &Config {
52        &self.config
53    }
54
55    pub async fn do_action<T, S1, S2>(
56        &self,
57        method: Method,
58        bucket_name: S1,
59        uri: S2,
60        with_headers: Option<HeaderMap>,
61        params: Option<HashMap<String, String>>,
62        body: Option<T>,
63    ) -> Result<Response, ObsError>
64    where
65        T: Into<Body> + Send,
66        S1: AsRef<str> + Send,
67        S2: AsRef<str> + Send,
68    {
69        let mut auth_headers = HashMap::new();
70        let mut headers = if let Some(wh) = with_headers {
71            for (k, v) in &wh {
72                if let Ok(v) = v.to_str() {
73                    auth_headers.insert(k.as_str().to_string(), vec![v.to_string()]);
74                }
75            }
76            wh
77        } else {
78            HeaderMap::new()
79        };
80
81        let (request_uri, canonicalized_url) =
82            self.config()
83                .format_urls(bucket_name.as_ref(), uri.as_ref(), params);
84
85        let url = format!(
86            "https://{}.{}/{}",
87            bucket_name.as_ref(),
88            self.config().endpoint(),
89            &request_uri
90        );
91
92        let auth_headers = self.auth(
93            method.as_str(),
94            bucket_name.as_ref(),
95            HashMap::new(),
96            auth_headers,
97            canonicalized_url,
98        )?;
99        headers.extend(auth_headers);
100
101        let mut req = self.http_client.request(method, url).headers(headers);
102
103        if let Some(body) = body {
104            req = req.body(body);
105        }
106        let res = req.send().await?;
107        Ok(res)
108    }
109
110    pub async fn do_action_without_bucket_name<T, S1>(
111        &self,
112        method: Method,
113        uri: S1,
114        with_headers: Option<HeaderMap>,
115        params: Option<HashMap<String, String>>,
116        body: Option<T>,
117    ) -> Result<Response, ObsError>
118    where
119        T: Into<Body> + Send,
120        S1: AsRef<str> + Send,
121    {
122        let (request_uri, canonicalized_url) = self.config().format_urls("", uri.as_ref(), params);
123        let mut headers = self.auth(
124            method.as_str(),
125            "",
126            HashMap::new(),
127            HashMap::new(),
128            canonicalized_url,
129        )?;
130        let url = format!("https://{}/{}", self.config().endpoint(), &request_uri);
131
132        if let Some(wh) = with_headers {
133            headers.extend(wh);
134        }
135
136        let mut req = self.http_client.request(method, url).headers(headers);
137
138        if let Some(body) = body {
139            req = req.body(body);
140        }
141        let res = req.send().await?;
142        Ok(res)
143    }
144}
145
146#[derive(Debug)]
147pub struct ClientBuilder {
148    config: Config,
149}
150
151impl Default for ClientBuilder {
152    fn default() -> Self {
153        Self::new()
154    }
155}
156
157impl ClientBuilder {
158    fn new() -> ClientBuilder {
159        ClientBuilder {
160            config: Config {
161                security_providers: vec![],
162                endpoint: "".into(),
163                is_secure: false,
164                region: "".into(),
165                timeout: Duration::from_secs(3),
166                signature_type: SignatureType::V2,
167            },
168        }
169    }
170    pub fn signature_type(mut self, st: SignatureType) -> ClientBuilder {
171        self.config.set_signature_type(st);
172        self
173    }
174
175    pub fn security_providers(mut self, sps: Vec<SecurityHolder>) -> ClientBuilder {
176        self.config.set_security_providers(sps);
177        self
178    }
179
180    /// 节点,支持以下三种格式:
181    ///
182    /// 1. http://your-endpoint
183    /// 2. https://your-endpoint
184    /// 3. your-endpoint
185    pub fn endpoint<S: ToString>(mut self, value: S) -> ClientBuilder {
186        let mut value = value.to_string();
187        if value.starts_with("https://") {
188            value = value.replace("https://", "");
189        } else if value.starts_with("http://") {
190            value = value.replace("http://", "");
191        }
192        self.config.set_endpoint(value);
193        self
194    }
195
196    pub fn security_provider<S: ToString>(mut self, ak: S, sk: S) -> ClientBuilder {
197        self.config.security_providers.push(SecurityHolder::new(
198            ak.to_string(),
199            sk.to_string(),
200            "".to_string(),
201        ));
202        self
203    }
204
205    pub fn timeout(mut self, duration: Duration) -> ClientBuilder {
206        self.config.set_timeout(duration);
207        self
208    }
209
210    pub fn region<S: ToString>(mut self, value: S) -> ClientBuilder {
211        self.config.set_region(value.to_string());
212        self
213    }
214
215    pub fn is_secure(mut self, value: bool) -> ClientBuilder {
216        self.config.set_is_secure(value);
217        self
218    }
219
220    pub fn build(self) -> Result<Client, ObsError> {
221        let req_client = reqwest::ClientBuilder::new()
222            .timeout(self.config.timeout())
223            .build();
224        Ok(Client {
225            config: self.config,
226            http_client: req_client?,
227        })
228    }
229}