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 { 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}