Skip to main content

toolcraft_s3_kit/
util.rs

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}