Skip to main content

rusticity_core/
s3.rs

1use crate::config::AwsConfig;
2use anyhow::Result;
3
4pub mod object;
5
6pub struct S3Client {
7    config: AwsConfig,
8}
9
10impl S3Client {
11    pub fn new(config: AwsConfig) -> Self {
12        Self { config }
13    }
14
15    pub async fn list_buckets(&self) -> Result<Vec<(String, String, String)>> {
16        let client = self.config.s3_client();
17        let resp = client.list_buckets().send().await?;
18
19        let buckets: Vec<(String, String, String)> = resp
20            .buckets()
21            .iter()
22            .filter_map(|b| {
23                let name = b.name()?.to_string();
24                let date = b.creation_date().map(|d| d.to_string()).unwrap_or_default();
25                Some((name, String::new(), date))
26            })
27            .collect();
28
29        Ok(buckets)
30    }
31
32    pub async fn get_bucket_location(&self, bucket: &str) -> Result<String> {
33        let client = self.config.s3_client();
34        let region = match client.get_bucket_location().bucket(bucket).send().await {
35            Ok(resp) => match resp.location_constraint() {
36                Some(loc) => {
37                    let loc_str = loc.as_str();
38                    if loc_str.is_empty() || loc_str == "null" {
39                        "us-east-1".to_string()
40                    } else {
41                        loc_str.to_string()
42                    }
43                }
44                None => "us-east-1".to_string(),
45            },
46            Err(_) => "us-east-1".to_string(),
47        };
48
49        Ok(region)
50    }
51
52    pub async fn list_objects(
53        &self,
54        bucket: &str,
55        bucket_region: &str,
56        prefix: &str,
57    ) -> Result<Vec<(String, i64, String, bool, String)>> {
58        let client = self.config.s3_client_with_region(bucket_region).await;
59        let mut req = client.list_objects_v2().bucket(bucket).delimiter("/");
60
61        if !prefix.is_empty() {
62            req = req.prefix(prefix);
63        }
64
65        let resp = req.send().await?;
66
67        let mut objects = Vec::new();
68
69        for prefix in resp.common_prefixes() {
70            if let Some(p) = prefix.prefix() {
71                objects.push((p.to_string(), 0, String::new(), true, String::new()));
72            }
73        }
74
75        for obj in resp.contents() {
76            if let Some(key) = obj.key() {
77                let size = obj.size().unwrap_or(0);
78                let modified = obj
79                    .last_modified()
80                    .map(|d| d.to_string())
81                    .unwrap_or_default();
82                let storage_class = obj
83                    .storage_class()
84                    .map(|s| s.as_str().to_string())
85                    .unwrap_or_else(|| "STANDARD".to_string());
86                objects.push((key.to_string(), size, modified, false, storage_class));
87            }
88        }
89
90        Ok(objects)
91    }
92}