Skip to main content

s3_wasi_http/api/
list_objects_v2.rs

1use std::i32;
2
3use anyhow::{Result, anyhow};
4use wstd::http::{Body, Method};
5use xml::{EventReader, reader::XmlEvent};
6
7use super::{
8    ApiObject, S3RequestBuilder, S3RequestData, S3ResponseData, parse_xml_bool, parse_xml_string,
9    parse_xml_value,
10};
11
12#[derive(Default)]
13pub struct ListObjectsV2Request {
14    pub token: Option<String>,
15    pub delimiter: Option<char>,
16    pub encoding_type: Option<String>,
17    pub fetch_owner: bool,
18    pub max_keys: Option<i32>,
19    pub start_after: Option<String>,
20}
21
22
23impl S3RequestData for ListObjectsV2Request {
24    type ResponseType = ListObjectsV2Response;
25
26    fn into_builder(
27        &self,
28        access_key: &str,
29        secret_key: &str,
30        region: &str,
31        endpoint: &str,
32    ) -> Result<S3RequestBuilder<Self::ResponseType>> {
33        let mut builder =
34            S3RequestBuilder::new(Method::GET, "/", access_key, secret_key, region, endpoint);
35        builder.query("list-type", Some("2"));
36
37        if let Some(token) = &self.token {
38            builder.query("continuation-token", Some(token));
39        }
40        if let Some(delimiter) = &self.delimiter {
41            builder.query("delimiter", Some(&delimiter.to_string()));
42        }
43        if let Some(encoding_type) = &self.encoding_type {
44            builder.query("encoding-type", Some(encoding_type));
45        }
46        if self.fetch_owner {
47            builder.query("fetch-owner", Some("true"));
48        }
49        if let Some(max_keys) = self.max_keys {
50            builder.query("max-keys", Some(&max_keys.to_string()));
51        }
52        if let Some(start_after) = &self.start_after {
53            builder.query("start-after", Some(start_after));
54        }
55
56        Ok(builder)
57    }
58}
59
60pub struct ListObjectsV2Response {
61    pub common_prefixes: Vec<String>,
62    pub contents: Vec<ApiObject>,
63    pub encoding_type: Option<String>,
64    pub is_truncated: bool,
65    pub key_count: i32,
66    pub max_keys: i32,
67    pub name: String,
68    pub continuation_token: Option<String>,
69    pub next_continuation_token: Option<String>,
70    pub prefix: Option<String>,
71    pub delimiter: Option<String>,
72    pub start_after: Option<String>,
73}
74
75impl S3ResponseData for ListObjectsV2Response {
76    async fn parse_body(response: &mut Body) -> Result<Self>
77    where
78        Self: Sized,
79    {
80        let data = response.contents().await?;
81        let mut parser = EventReader::new(data);
82
83        let mut list_objects_response = ListObjectsV2Response {
84            common_prefixes: Vec::new(),
85            contents: Vec::new(),
86            encoding_type: None,
87            is_truncated: false,
88            key_count: 0,
89            max_keys: 0,
90            name: String::new(),
91            continuation_token: None,
92            next_continuation_token: None,
93            prefix: None,
94            delimiter: None,
95            start_after: None,
96        };
97        loop {
98            let element = parser.next()?;
99
100            match element {
101                xml::reader::XmlEvent::EndDocument => break,
102
103                xml::reader::XmlEvent::StartElement { name, .. }
104                    if name.local_name == "IsTruncated" =>
105                {
106                    list_objects_response.is_truncated =
107                        parse_xml_bool(&mut parser, "IsTruncated")?;
108                }
109                xml::reader::XmlEvent::StartElement { name, .. } if name.local_name == "Name" => {
110                    list_objects_response.name = parse_xml_string(&mut parser, "Name")?;
111                }
112                xml::reader::XmlEvent::StartElement { name, .. } if name.local_name == "Prefix" => {
113                    if let XmlEvent::Characters(value) = parser.next()? {
114                        list_objects_response.prefix = Some(value);
115                    }
116                }
117                xml::reader::XmlEvent::StartElement { name, .. }
118                    if name.local_name == "Delimiter" =>
119                {
120                    list_objects_response.delimiter =
121                        Some(parse_xml_string(&mut parser, "Delimiter")?);
122                }
123                xml::reader::XmlEvent::StartElement { name, .. }
124                    if name.local_name == "MaxKeys" =>
125                {
126                    list_objects_response.max_keys =
127                        parse_xml_value::<i32>(&mut parser, "MaxKeys")?;
128                }
129                xml::reader::XmlEvent::StartElement { name, .. }
130                    if name.local_name == "EncodingType" =>
131                {
132                    list_objects_response.encoding_type =
133                        Some(parse_xml_string(&mut parser, "EncodingType")?);
134                }
135                xml::reader::XmlEvent::StartElement { name, .. }
136                    if name.local_name == "KeyCount" =>
137                {
138                    list_objects_response.key_count =
139                        parse_xml_value::<i32>(&mut parser, "KeyCount")?;
140                }
141                xml::reader::XmlEvent::StartElement { name, .. }
142                    if name.local_name == "ContinuationToken" =>
143                {
144                    list_objects_response.continuation_token =
145                        Some(parse_xml_string(&mut parser, "ContinuationToken")?);
146                }
147                xml::reader::XmlEvent::StartElement { name, .. }
148                    if name.local_name == "NextContinuationToken" =>
149                {
150                    list_objects_response.next_continuation_token =
151                        Some(parse_xml_string(&mut parser, "NextContinuationToken")?);
152                }
153                xml::reader::XmlEvent::StartElement { name, .. }
154                    if name.local_name == "StartAfter" =>
155                {
156                    list_objects_response.next_continuation_token =
157                        Some(parse_xml_string(&mut parser, "StartAfter")?);
158                }
159
160                xml::reader::XmlEvent::StartElement { name, .. }
161                    if name.local_name == "CommonPrefixes" =>
162                {
163                    if let XmlEvent::StartElement { name, .. } = parser.next()? {
164                        if name.local_name == "Prefix" {
165                            if let XmlEvent::Characters(value) = parser.next()? {
166                                list_objects_response.common_prefixes.push(value);
167                            } else {
168                                return Err(anyhow!(
169                                    "Invalid response object, CommonPrefixes.Prefix has no value"
170                                ));
171                            }
172                        } else {
173                            return Err(anyhow!(
174                                "Invalid response object, CommonPrefixes has no value"
175                            ));
176                        }
177                    } else {
178                        return Err(anyhow!(
179                            "Invalid response object, CommonPrefixes has no value"
180                        ));
181                    }
182                }
183                xml::reader::XmlEvent::StartElement { name, .. }
184                    if name.local_name == "Contents" =>
185                {
186                    list_objects_response
187                        .contents
188                        .push(ApiObject::parse(&mut parser)?);
189                }
190
191                _ => {}
192            }
193        }
194
195        Ok(list_objects_response)
196    }
197}