1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use crate::common::body_to_bytes;
use crate::{
Error,
common::{Owner, StorageClass},
error::normal_error,
request::{Oss, OssRequest},
};
use http::Method;
use serde_derive::Deserialize;
use std::cmp;
// Returned content
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ObjectsList {
// Continuation token for subsequent requests
pub next_continuation_token: Option<String>,
// File list
pub contents: Option<Vec<ObjectInfo>>,
// Group list
pub common_prefixes: Option<Vec<CommonPrefixes>>,
}
/// Object information
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct ObjectInfo {
/// Object path
pub key: String,
/// Last modified time of the object
pub last_modified: String,
/// The ETag is generated for each object to identify its content. It can be used to check if the object has changed, but it is not recommended to use it as an MD5 checksum for data integrity.
pub e_tag: String,
#[serde(rename = "Type")]
pub type_field: String,
/// Size of the object in bytes
pub size: u64,
/// Storage class of the object
pub storage_class: StorageClass,
/// Restore status of the object
pub restore_info: Option<String>,
/// Owner information of the bucket
pub owner: Option<Owner>,
}
/// Group list
#[derive(Debug, Deserialize)]
#[serde(rename_all = "PascalCase")]
pub struct CommonPrefixes {
/// Prefix
pub prefix: String,
}
/// List information of all files in the bucket
///
/// By default, retrieves the first 1000 files
///
/// See the [Alibaba Cloud documentation](https://help.aliyun.com/document_detail/187544.html) for details
pub struct ListObjects {
req: OssRequest,
}
impl ListObjects {
pub(super) fn new(oss: Oss) -> Self {
let mut req = OssRequest::new(oss, Method::GET);
req.insert_query("list-type", "2");
req.insert_query("max-keys", "1000");
ListObjects { req }
}
/// Character used to group object names. All object names containing the specified prefix are grouped between the first occurrences of the delimiter (i.e., CommonPrefixes)
pub fn set_delimiter(mut self, delimiter: impl ToString) -> Self {
self.req.insert_query("delimiter", delimiter);
self
}
/// Specify where to start listing objects alphabetically after start-after.
///
/// start-after is used for pagination and must be less than 1024 bytes.
///
/// When performing conditional queries, even if start-after does not exist, listing starts from the next object in alphabetical order.
pub fn set_start_after(mut self, start_after: impl ToString) -> Self {
self.req.insert_query("start-after", start_after);
self
}
/// Specify the token from which the listing operation should begin.
///
/// This token can be obtained from NextContinuationToken in the ListObjects result.
pub fn set_continuation_token(mut self, continuation_token: impl ToString) -> Self {
self.req
.insert_query("continuation-token", continuation_token);
self
}
/// Restrict the returned object keys to those with the given prefix.
pub fn set_prefix(mut self, prefix: impl ToString) -> Self {
self.req.insert_query("prefix", prefix.to_string());
self
}
/// Specify the maximum number of files to return.
///
/// When a delimiter is set, this counts both files and groups
///
/// Default: 1000, range 1-1000; values outside the range use the default
pub fn set_max_keys(mut self, max_keys: u32) -> Self {
let max_keys = cmp::min(1000, cmp::max(1, max_keys));
self.req.insert_query("max-keys", max_keys);
self
}
/// Specify whether to include owner information in the result.
pub fn fetch_owner(mut self) -> Self {
self.req.insert_query("fetch-owner", "true");
self
}
/// Send the request
///
pub async fn send(self) -> Result<ObjectsList, Error> {
// Build the HTTP request
let response = self.req.send_to_oss()?.await?;
// Parse the response
let status_code = response.status();
match status_code {
code if code.is_success() => {
let response_bytes = body_to_bytes(response.into_body())
.await
.map_err(|_| Error::OssInvalidResponse(None))?;
let object_list: ObjectsList = serde_xml_rs::from_reader(&*response_bytes)
.map_err(|_| Error::OssInvalidResponse(Some(response_bytes)))?;
Ok(object_list)
}
_ => return Err(normal_error(response).await),
}
}
}