Skip to main content

toolcraft_s3_kit/
util.rs

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}