oss_rust_sdk/
object.rs

1use quick_xml::{events::Event, Reader};
2use reqwest::header::{HeaderMap, HeaderValue, DATE};
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use crate::auth::Auth;
7use crate::oss::RequestType;
8
9use super::errors::{Error, ObjectError};
10use super::oss::OSS;
11use super::utils::*;
12
13#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(rename_all = "PascalCase")]
15pub struct CommonPrefix {
16    prefix: String,
17}
18
19impl CommonPrefix {
20    pub fn new(prefix: String) -> Self {
21        Self { prefix }
22    }
23
24    pub fn prefix(&self) -> &str {
25        &self.prefix
26    }
27}
28
29#[derive(Clone, Debug, Serialize, Deserialize)]
30#[serde(rename_all = "PascalCase")]
31pub struct ListObjects {
32    name: String,
33    delimiter: String,
34    prefix: String,
35    marker: String,
36    max_keys: String,
37    is_truncated: bool,
38
39    #[serde(default)]
40    contents: Vec<Object>,
41    #[serde(default)]
42    common_prefixes: Vec<CommonPrefix>,
43}
44
45impl ListObjects {
46    pub fn new(
47        name: String,
48        delimiter: String,
49        prefix: String,
50        marker: String,
51        max_keys: String,
52        is_truncated: bool,
53
54        contents: Vec<Object>,
55        common_prefixes: Vec<CommonPrefix>,
56    ) -> Self {
57        ListObjects {
58            name,
59            delimiter,
60            prefix,
61            marker,
62            max_keys,
63            is_truncated,
64
65            contents,
66            common_prefixes,
67        }
68    }
69
70    pub fn name(&self) -> &str {
71        &self.name
72    }
73
74    pub fn delimiter(&self) -> &str {
75        &self.delimiter
76    }
77
78    pub fn prefix(&self) -> &str {
79        &self.prefix
80    }
81
82    pub fn marker(&self) -> &str {
83        &self.marker
84    }
85
86    pub fn max_keys(&self) -> &str {
87        &self.max_keys
88    }
89
90    pub fn is_truncated(&self) -> bool {
91        self.is_truncated
92    }
93
94    pub fn contents(&self) -> &Vec<Object> {
95        &self.contents
96    }
97
98    pub fn common_prefixes(&self) -> &Vec<CommonPrefix> {
99        &self.common_prefixes
100    }
101}
102
103#[derive(Clone, Debug, Serialize, Deserialize, Default)]
104#[serde(rename_all = "PascalCase")]
105pub struct Owner {
106    #[serde(alias = "ID")]
107    pub id: String,
108    pub display_name: String,
109}
110
111#[derive(Clone, Debug, Serialize, Deserialize)]
112#[serde(rename_all = "PascalCase")]
113pub struct Object {
114    key: String,
115    last_modified: String,
116    size: usize,
117    e_tag: String,
118    r#type: String,
119    storage_class: String,
120    owner: Owner,
121}
122
123impl Object {
124    pub fn new(
125        key: String,
126        last_modified: String,
127        size: usize,
128
129        e_tag: String,
130        r#type: String,
131        storage_class: String,
132        owner: Owner,
133    ) -> Self {
134        Object {
135            key,
136            last_modified,
137            size,
138            e_tag,
139            r#type,
140            storage_class,
141            owner,
142        }
143    }
144
145    pub fn key(&self) -> &str {
146        &self.key
147    }
148
149    pub fn last_modified(&self) -> &str {
150        &self.last_modified
151    }
152
153    pub fn size(&self) -> usize {
154        self.size
155    }
156
157    pub fn e_tag(&self) -> &str {
158        &self.e_tag
159    }
160
161    pub fn r#type(&self) -> &str {
162        &self.r#type
163    }
164
165    pub fn storage_class(&self) -> &str {
166        &self.storage_class
167    }
168
169    pub fn id(&self) -> &str {
170        &self.owner.id
171    }
172
173    pub fn display_name(&self) -> &str {
174        &self.owner.display_name
175    }
176}
177
178trait PrivateObjectAPI {
179    fn generate_presigned_path<S1>(&self, object_name: S1, expires: usize) -> String
180    where
181        S1: AsRef<str> + Send;
182}
183
184pub trait ObjectAPI {
185    fn list_object<S, H, R>(&self, headers: H, resources: R) -> Result<ListObjects, Error>
186    where
187        S: AsRef<str>,
188        H: Into<Option<HashMap<S, S>>>,
189        R: Into<Option<HashMap<S, Option<S>>>>;
190
191    fn get_object<S1, S2, H, R>(
192        &self,
193        object_name: S1,
194        headers: H,
195        resources: R,
196    ) -> Result<Vec<u8>, Error>
197    where
198        S1: AsRef<str>,
199        S2: AsRef<str>,
200        H: Into<Option<HashMap<S2, S2>>>,
201        R: Into<Option<HashMap<S2, Option<S2>>>>;
202
203    fn get_object_acl<S>(&self, object_name: S) -> Result<String, Error>
204    where
205        S: AsRef<str>;
206
207    fn put_object_from_file<S1, S2, S3, H, R>(
208        &self,
209        file: S1,
210        object_name: S2,
211        headers: H,
212        resources: R,
213    ) -> Result<(), Error>
214    where
215        S1: AsRef<str>,
216        S2: AsRef<str>,
217        S3: AsRef<str>,
218        H: Into<Option<HashMap<S3, S3>>>,
219        R: Into<Option<HashMap<S3, Option<S3>>>>;
220
221    fn put_object_from_buffer<S1, S2, H, R>(
222        &self,
223        buf: &[u8],
224        object_name: S1,
225        headers: H,
226        resources: R,
227    ) -> Result<(), Error>
228    where
229        S1: AsRef<str>,
230        S2: AsRef<str>,
231        H: Into<Option<HashMap<S2, S2>>>,
232        R: Into<Option<HashMap<S2, Option<S2>>>>;
233
234    fn get_object_signed_url<S1>(&self, object_name: S1, expires: usize) -> String
235    where
236        S1: AsRef<str> + Send;
237
238    fn copy_object_from_object<S1, S2, S3, H, R>(
239        &self,
240        src: S1,
241        dest: S2,
242        headers: H,
243        resources: R,
244    ) -> Result<(), Error>
245    where
246        S1: AsRef<str>,
247        S2: AsRef<str>,
248        S3: AsRef<str>,
249        H: Into<Option<HashMap<S3, S3>>>,
250        R: Into<Option<HashMap<S3, Option<S3>>>>;
251
252    fn delete_object<S>(&self, object_name: S) -> Result<(), Error>
253    where
254        S: AsRef<str>;
255}
256
257impl<'a> PrivateObjectAPI for OSS<'a> {
258    fn generate_presigned_path<S1>(&self, object_name: S1, expires: usize) -> String
259    where
260        S1: AsRef<str> + Send,
261    {
262        let object_name = object_name.as_ref();
263        let mut headers = HeaderMap::new();
264        headers.insert(DATE, HeaderValue::from_str(&expires.to_string()).unwrap());
265        let signature = self.sign(
266            RequestType::Get.as_str(),
267            self.key_secret(),
268            self.bucket(),
269            object_name,
270            "",
271            &headers,
272        );
273        format!(
274            "/{}?Expires={}&OSSAccessKeyId={}&Signature={}",
275            urlencoding::encode(object_name),
276            expires,
277            urlencoding::encode(self.key_id()),
278            urlencoding::encode(&signature)
279        )
280    }
281}
282
283impl<'a> ObjectAPI for OSS<'a> {
284    fn list_object<S, H, R>(&self, headers: H, resources: R) -> Result<ListObjects, Error>
285    where
286        S: AsRef<str>,
287        H: Into<Option<HashMap<S, S>>>,
288        R: Into<Option<HashMap<S, Option<S>>>>,
289    {
290        let (host, headers) =
291            self.build_request(RequestType::Get, String::new(), headers, resources)?;
292
293        let resp = reqwest::blocking::Client::new()
294            .get(&host)
295            .headers(headers)
296            .send()?;
297
298        let body = resp.text()?;
299        let list_objects = quick_xml::de::from_str::<ListObjects>(&body)?;
300
301        Ok(list_objects)
302    }
303
304    fn get_object<S1, S2, H, R>(
305        &self,
306        object_name: S1,
307        headers: H,
308        resources: R,
309    ) -> Result<Vec<u8>, Error>
310    where
311        S1: AsRef<str>,
312        S2: AsRef<str>,
313        H: Into<Option<HashMap<S2, S2>>>,
314        R: Into<Option<HashMap<S2, Option<S2>>>>,
315    {
316        let (host, headers) =
317            self.build_request(RequestType::Get, object_name, headers, resources)?;
318
319        let mut resp = reqwest::blocking::Client::new()
320            .get(&host)
321            .headers(headers)
322            .send()?;
323        let mut buf: Vec<u8> = vec![];
324
325        if resp.status().is_success() {
326            resp.copy_to(&mut buf)?;
327            Ok(buf)
328        } else {
329            Err(Error::Object(ObjectError::GetError {
330                msg: format!("can not get object, status code: {}", resp.status()).into(),
331            }))
332        }
333    }
334
335    fn get_object_acl<S>(&self, object_name: S) -> Result<String, Error>
336    where
337        S: AsRef<str>,
338    {
339        let object_name = object_name.as_ref();
340        let mut params: HashMap<&str, Option<&str>> = HashMap::new();
341        params.insert("acl", None);
342        let result = String::from_utf8(self.get_object(object_name, None, Some(params))?)?;
343        let mut reader = Reader::from_str(&result);
344        reader.trim_text(true);
345        let mut grant = String::new();
346
347        loop {
348            match reader.read_event() {
349                Ok(Event::Start(ref e)) if e.name().as_ref() == b"Grant" => {
350                    grant = reader.read_text(e.name())?.to_string();
351                }
352                Ok(Event::Eof) => break,
353                Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
354                _ => (),
355            }
356        }
357
358        Ok(grant)
359    }
360
361    fn get_object_signed_url<S1>(&self, object_name: S1, expires: usize) -> String
362    where
363        S1: AsRef<str> + Send,
364    {
365        format!(
366            "https://{}.{}{}",
367            self.bucket(),
368            self.endpoint(),
369            self.generate_presigned_path(object_name, expires),
370        )
371    }
372
373    fn put_object_from_file<S1, S2, S3, H, R>(
374        &self,
375        file: S1,
376        object_name: S2,
377        headers: H,
378        resources: R,
379    ) -> Result<(), Error>
380    where
381        S1: AsRef<str>,
382        S2: AsRef<str>,
383        S3: AsRef<str>,
384        H: Into<Option<HashMap<S3, S3>>>,
385        R: Into<Option<HashMap<S3, Option<S3>>>>,
386    {
387        let (host, headers) =
388            self.build_request(RequestType::Put, object_name, headers, resources)?;
389
390        let buf = load_file(file)?;
391
392        let resp = reqwest::blocking::Client::new()
393            .put(&host)
394            .headers(headers)
395            .body(buf)
396            .send()?;
397
398        if resp.status().is_success() {
399            Ok(())
400        } else {
401            Err(Error::Object(ObjectError::PutError {
402                msg: format!("can not put object, status code: {}", resp.status()).into(),
403            }))
404        }
405    }
406
407    fn put_object_from_buffer<S1, S2, H, R>(
408        &self,
409        buf: &[u8],
410        object_name: S1,
411        headers: H,
412        resources: R,
413    ) -> Result<(), Error>
414    where
415        S1: AsRef<str>,
416        S2: AsRef<str>,
417        H: Into<Option<HashMap<S2, S2>>>,
418        R: Into<Option<HashMap<S2, Option<S2>>>>,
419    {
420        let (host, headers) =
421            self.build_request(RequestType::Put, object_name, headers, resources)?;
422
423        let resp = reqwest::blocking::Client::new()
424            .put(&host)
425            .headers(headers)
426            .body(buf.to_owned())
427            .send()?;
428
429        if resp.status().is_success() {
430            Ok(())
431        } else {
432            Err(Error::Object(ObjectError::PutError {
433                msg: format!("can not put object, status code: {}", resp.status()).into(),
434            }))
435        }
436    }
437
438    fn copy_object_from_object<S1, S2, S3, H, R>(
439        &self,
440        src: S1,
441        object_name: S2,
442        headers: H,
443        resources: R,
444    ) -> Result<(), Error>
445    where
446        S1: AsRef<str>,
447        S2: AsRef<str>,
448        S3: AsRef<str>,
449        H: Into<Option<HashMap<S3, S3>>>,
450        R: Into<Option<HashMap<S3, Option<S3>>>>,
451    {
452        let (host, mut headers) =
453            self.build_request(RequestType::Put, object_name, headers, resources)?;
454        headers.insert("x-oss-copy-source", src.as_ref().parse()?);
455
456        let resp = reqwest::blocking::Client::new()
457            .put(&host)
458            .headers(headers)
459            .send()?;
460
461        if resp.status().is_success() {
462            Ok(())
463        } else {
464            Err(Error::Object(ObjectError::CopyError {
465                msg: format!("can not copy object, status code: {}", resp.status()).into(),
466            }))
467        }
468    }
469
470    fn delete_object<S>(&self, object_name: S) -> Result<(), Error>
471    where
472        S: AsRef<str>,
473    {
474        let headers = HashMap::<String, String>::new();
475        let (host, headers) =
476            self.build_request(RequestType::Delete, object_name, Some(headers), None)?;
477
478        let resp = reqwest::blocking::Client::new()
479            .delete(&host)
480            .headers(headers)
481            .send()?;
482
483        if resp.status().is_success() {
484            Ok(())
485        } else {
486            Err(Error::Object(ObjectError::DeleteError {
487                msg: format!("can not delete object, status code: {}", resp.status()).into(),
488            }))
489        }
490    }
491}