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