rustfs_rsc/client/
executor.rs

1use bytes::Bytes;
2use hyper::header::{HeaderName, HeaderValue};
3use hyper::{HeaderMap, Method};
4use reqwest::Response;
5
6use super::{Minio, QueryMap};
7use crate::data::Data;
8use crate::datatype::{FromXml, ToXml};
9use crate::error::{Error, Result, S3Error};
10use crate::utils::md5sum_hash;
11
12/// An executor builds the S3 request.
13/// ```rust
14/// use hyper::Method;
15/// use bytes::Bytes;
16/// use reqwest::Response;
17/// use minio_rsc::Minio;
18/// use minio_rsc::error::Result;
19///
20/// async fn get_object(minio:Minio)-> Result<Response> {
21///     let executor = minio.executor(Method::GET);
22///     let res: Response = executor
23///         .bucket_name("bucket")
24///         .object_name("test.txt")
25///         .query("versionId", "cdabf31a-9752-4265-b137-6b3961fbaf9b")
26///         .send_ok()
27///         .await?;
28///     Ok(res)
29/// }
30///
31/// async fn put_object(minio:Minio, data:Bytes)-> Result<()> {
32///     let executor = minio.executor(Method::PUT);
33///     let res: Response = executor
34///         .bucket_name("bucket")
35///         .object_name("test.txt")
36///         .body(data)
37///         .send_ok()
38///         .await?;
39///     Ok(())
40/// }
41/// ```
42pub struct BaseExecutor<'a> {
43    method: Method,
44    region: String,
45    bucket_name: Option<String>,
46    object_name: Option<String>,
47    body: Data<Error>,
48    headers: HeaderMap,
49    querys: QueryMap,
50    client: &'a Minio,
51    build_err: Result<()>,
52}
53
54impl<'a> BaseExecutor<'a> {
55    pub fn new(method: Method, client: &'a Minio) -> Self {
56        return Self {
57            method,
58            region: client.region().to_string(),
59            bucket_name: None,
60            object_name: None,
61            body: Default::default(),
62            headers: HeaderMap::new(),
63            client,
64            querys: QueryMap::new(),
65            build_err: Ok(()),
66        };
67    }
68
69    /// Set the request method.
70    pub fn method(mut self, method: Method) -> Self {
71        self.method = method;
72        self
73    }
74
75    /// Set the bucket name.
76    pub fn bucket_name<T: Into<String>>(mut self, name: T) -> Self {
77        self.bucket_name = Some(name.into());
78        self
79    }
80
81    /// Set the object name.
82    pub fn object_name<T: Into<String>>(mut self, name: T) -> Self {
83        self.object_name = Some(name.into());
84        self
85    }
86
87    /// Set the region.
88    pub fn region<T: Into<String>>(mut self, region: T) -> Self {
89        self.region = region.into();
90        self
91    }
92
93    /// Set the request body.
94    pub fn body<B: Into<Data<Error>>>(mut self, body: B) -> Self {
95        self.body = body.into();
96        self
97    }
98
99    /// Set the xml struct to body and set md5 header.
100    pub(crate) fn xml<'de, S>(mut self, xml: &'de S) -> Self
101    where
102        S: ToXml,
103    {
104        let xml = match xml.to_xml() {
105            Ok(xml) => xml,
106            Err(e) => {
107                self.build_err = Err(e);
108                return self;
109            }
110        };
111        let body = Bytes::from(xml);
112        let md5 = md5sum_hash(&body);
113        self.body(body).header("Content-MD5", md5)
114    }
115
116    /// Set the new request header.
117    pub fn headers(mut self, header: HeaderMap) -> Self {
118        self.headers = header;
119        self
120    }
121
122    /// Inserts a key-value pair into the request header.
123    pub fn header<K, V>(mut self, key: K, value: V) -> Self
124    where
125        HeaderName: TryFrom<K>,
126        <HeaderName as TryFrom<K>>::Error: Into<crate::error::Error>,
127        HeaderValue: TryFrom<V>,
128        <HeaderValue as TryFrom<V>>::Error: Into<crate::error::Error>,
129    {
130        let key = <HeaderName as TryFrom<K>>::try_from(key).map_err(Into::into);
131        let value = <HeaderValue as TryFrom<V>>::try_from(value).map_err(Into::into);
132        match (key, value) {
133            (Ok(key), Ok(val)) => {
134                self.headers.insert(key, val);
135            }
136            (Err(e), _) => self.build_err = Err(e),
137            (_, Err(e)) => self.build_err = Err(e),
138        };
139        self
140    }
141
142    /// Merge header into request header.
143    #[inline]
144    pub fn headers_merge(mut self, header: HeaderMap) -> Self {
145        self.headers.extend(header);
146        self
147    }
148
149    /// Merge header into request header.
150    #[inline]
151    pub fn headers_merge2(self, header: Option<HeaderMap>) -> Self {
152        if let Some(header) = header {
153            self.headers_merge(header)
154        } else {
155            self
156        }
157    }
158
159    /// Set up a new request query.
160    pub fn querys(mut self, querys: QueryMap) -> Self {
161        self.querys = querys;
162        self
163    }
164
165    /// Merge querys into request query.
166    pub fn querys_merge(mut self, querys: QueryMap) -> Self {
167        self.querys.merge(querys);
168        self
169    }
170
171    /// Inserts a key-value pair into the query map.
172    pub fn query<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
173        self.querys.insert(key.into(), value.into());
174        self
175    }
176
177    /// Inserts query_string into the query map.
178    pub fn query_string(mut self, query_str: &str) -> Self {
179        self.querys.merge_str(query_str);
180        self
181    }
182
183    pub fn apply<F>(self, apply: F) -> Self
184    where
185        F: FnOnce(Self) -> Self,
186    {
187        apply(self)
188    }
189
190    /// Send an HTTP request to S3 and return a Result<[Response]>.
191    ///
192    /// note: this is just a response from the s3 service, probably a wrong response.
193    pub async fn send(self) -> Result<Response> {
194        self.build_err?;
195        let query = self.querys.to_query_string();
196        self.client
197            ._execute(
198                self.method,
199                &self.region,
200                self.bucket_name,
201                self.object_name,
202                self.body,
203                Some(self.headers),
204                Some(query),
205            )
206            .await
207    }
208
209    /// Send an HTTP request to S3 and return a Result<[Response]>.
210    ///
211    /// This checks if the request is a legitimate S3 response.
212    pub async fn send_ok(self) -> Result<Response> {
213        let res = self.send().await?;
214        if res.status().is_success() {
215            Ok(res)
216        } else {
217            let text = res.text().await?;
218            let s: S3Error = text.as_str().try_into()?;
219            Err(s)?
220        }
221    }
222
223    /// Send an HTTP request to S3 and return a Result<[String]>.
224    ///
225    /// This checks if the request is a legitimate S3 response.
226    pub async fn send_text_ok(self) -> Result<String> {
227        let res = self.send_ok().await?;
228        let text = res.text().await?;
229        Ok(text)
230    }
231
232    /// Send an HTTP request to S3 and conver to xml struct.
233    ///
234    /// This checks if the request is a legitimate S3 response.
235    pub(crate) async fn send_xml_ok<T>(self) -> Result<T>
236    where
237        T: FromXml,
238    {
239        self.send_text_ok()
240            .await
241            .map(T::from_xml)?
242            .map_err(Into::into)
243    }
244}