s3_wasi_http/api/
list_objects_v2.rs1use 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}