1use crate::error::{Error, Result};
2
3pub struct ObjectInfo {
4 pub key: String,
5 pub size: u64,
6 pub last_modified: String,
7}
8
9pub(crate) async fn check_status(resp: reqwest::Response) -> Result<reqwest::Response> {
10 let status = resp.status();
11 if status.is_success() || status == reqwest::StatusCode::NO_CONTENT {
12 return Ok(resp);
13 }
14 let code = status.as_u16();
15 let body = resp.text().await.unwrap_or_default();
16 let message = extract_tag_values(&body, "Message")
17 .into_iter()
18 .next()
19 .unwrap_or(body);
20 Err(Error::S3 {
21 status: code,
22 message,
23 })
24}
25
26pub(crate) fn url_encode(s: &str) -> String {
27 let mut out = String::with_capacity(s.len());
28 for byte in s.bytes() {
29 match byte {
30 b'A' ..= b'Z' | b'a' ..= b'z' | b'0' ..= b'9' | b'-' | b'_' | b'.' | b'~' => {
31 out.push(byte as char);
32 }
33 _ => out.push_str(&format!("%{byte:02X}")),
34 }
35 }
36 out
37}
38
39pub(crate) fn extract_tag_values(xml: &str, tag: &str) -> Vec<String> {
40 let open = format!("<{tag}>");
41 let close = format!("</{tag}>");
42 let mut values = Vec::new();
43 let mut pos = 0;
44 while let Some(start) = xml[pos ..].find(&open) {
45 let content_start = pos + start + open.len();
46 if let Some(end) = xml[content_start ..].find(&close) {
47 values.push(xml[content_start .. content_start + end].to_string());
48 pos = content_start + end + close.len();
49 } else {
50 break;
51 }
52 }
53 values
54}
55
56pub(crate) fn parse_bucket_names(xml: &str) -> Result<Vec<String>> {
57 let buckets = extract_tag_values(xml, "Bucket");
58 Ok(buckets
59 .into_iter()
60 .filter_map(|b| extract_tag_values(&b, "Name").into_iter().next())
61 .collect())
62}
63
64pub(crate) fn parse_object_list(xml: &str) -> Result<Vec<ObjectInfo>> {
65 let contents = extract_tag_values(xml, "Contents");
66 Ok(contents
67 .into_iter()
68 .map(|block| ObjectInfo {
69 key: extract_tag_values(&block, "Key")
70 .into_iter()
71 .next()
72 .unwrap_or_default(),
73 size: extract_tag_values(&block, "Size")
74 .into_iter()
75 .next()
76 .and_then(|s| s.parse().ok())
77 .unwrap_or(0),
78 last_modified: extract_tag_values(&block, "LastModified")
79 .into_iter()
80 .next()
81 .unwrap_or_default(),
82 })
83 .collect())
84}