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