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 { status: code, message })
21}
22
23pub(crate) fn url_encode(s: &str) -> String {
24 let mut out = String::with_capacity(s.len());
25 for byte in s.bytes() {
26 match byte {
27 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
28 out.push(byte as char);
29 }
30 _ => out.push_str(&format!("%{byte:02X}")),
31 }
32 }
33 out
34}
35
36pub(crate) fn extract_tag_values(xml: &str, tag: &str) -> Vec<String> {
37 let open = format!("<{tag}>");
38 let close = format!("</{tag}>");
39 let mut values = Vec::new();
40 let mut pos = 0;
41 while let Some(start) = xml[pos..].find(&open) {
42 let content_start = pos + start + open.len();
43 if let Some(end) = xml[content_start..].find(&close) {
44 values.push(xml[content_start..content_start + end].to_string());
45 pos = content_start + end + close.len();
46 } else {
47 break;
48 }
49 }
50 values
51}
52
53pub(crate) fn parse_bucket_names(xml: &str) -> Result<Vec<String>> {
54 let buckets = extract_tag_values(xml, "Bucket");
55 Ok(buckets
56 .into_iter()
57 .filter_map(|b| extract_tag_values(&b, "Name").into_iter().next())
58 .collect())
59}
60
61pub(crate) fn parse_object_list(xml: &str) -> Result<Vec<ObjectInfo>> {
62 let contents = extract_tag_values(xml, "Contents");
63 Ok(contents
64 .into_iter()
65 .map(|block| ObjectInfo {
66 key: extract_tag_values(&block, "Key")
67 .into_iter()
68 .next()
69 .unwrap_or_default(),
70 size: extract_tag_values(&block, "Size")
71 .into_iter()
72 .next()
73 .and_then(|s| s.parse().ok())
74 .unwrap_or(0),
75 last_modified: extract_tag_values(&block, "LastModified")
76 .into_iter()
77 .next()
78 .unwrap_or_default(),
79 })
80 .collect())
81}