storage_client_interface/
s3.rs1use async_trait::async_trait;
2use aws_config::meta::region::RegionProviderChain;
3use aws_sdk_s3 as s3;
4use s3::{
5 error::SdkError,
6 operation::{
7 delete_object::DeleteObjectError, get_object::GetObjectError, put_object::PutObjectError,
8 },
9 primitives::ByteStream,
10 Client,
11};
12use thiserror::Error;
13
14use super::{StorageClientError, StorageClientInterface};
15
16#[derive(Error, Debug)]
17pub enum ClientError {
18 #[error("GetObject Error: {0}")]
19 GetObject(#[from] SdkError<GetObjectError>),
20 #[error("PutObject Error: {0}")]
21 PutObject(#[from] SdkError<PutObjectError>),
22 #[error("DeleteObject Error: {0}")]
23 DeleteObject(#[from] SdkError<DeleteObjectError>),
24 #[error("S3 Client Error - {0}")]
25 General(String),
26}
27
28impl From<ClientError> for StorageClientError {
29 fn from(error: ClientError) -> Self {
30 match error {
31 ClientError::GetObject(err) => StorageClientError::GetObject(err.to_string()),
32 ClientError::PutObject(err) => StorageClientError::PutObject(err.to_string()),
33 ClientError::DeleteObject(err) => StorageClientError::DeleteObject(err.to_string()),
34 ClientError::General(err) => StorageClientError::General(err),
35 }
36 }
37}
38#[derive(Clone, Debug)]
39pub struct StorageClient {
40 bucket: String,
41 client: s3::Client,
42}
43
44impl StorageClient {
45 #[allow(unused)]
46 pub async fn new(bucket: String) -> Self {
47 let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
48 let config = aws_config::from_env().region(region_provider).load().await;
49 let client = Client::new(&config);
50 Self { bucket, client }
51 }
52}
53
54#[async_trait]
55impl StorageClientInterface for StorageClient {
56 async fn get_object(&self, key: String) -> Result<Option<String>, StorageClientError> {
57 let object_res = self
58 .client
59 .get_object()
60 .bucket(&self.bucket)
61 .key(key)
62 .send()
63 .await;
64
65 let object = match object_res {
66 Ok(object) => object,
67 Err(err) => match err.into_service_error() {
68 GetObjectError::NoSuchKey(_) => return Ok(None),
69 err => {
70 println!("Error getting object from S3: {:?}", err);
71 return Err(StorageClientError::GetObject(err.to_string()));
72 }
73 },
74 };
75
76 let body_bytes = object
77 .body
78 .collect()
79 .await
80 .map_err(|err| ClientError::General(err.to_string()))?
81 .to_vec();
82
83 let body = String::from_utf8(body_bytes)
84 .map_err(|err| ClientError::General(format!("Failed to parse object body: {err}")))?;
85
86 Ok(Some(body))
87 }
88
89 async fn put_object(&self, key: String, body: String) -> Result<(), StorageClientError> {
90 let body_bytes = body.as_bytes().to_vec();
91
92 let _ = self
93 .client
94 .put_object()
95 .bucket(&self.bucket)
96 .key(key)
97 .body(ByteStream::from(body_bytes))
98 .send()
99 .await
100 .map_err(|err| {
101 println!("Error puttin object to S3: {:?}", err);
102 ClientError::PutObject(err)
103 })?;
104
105 Ok(())
106 }
107
108 async fn delete_object(&self, key: String) -> Result<(), StorageClientError> {
109 let _ = self
110 .client
111 .delete_object()
112 .bucket(&self.bucket)
113 .key(key)
114 .send()
115 .await
116 .map_err(|err| {
117 println!("Error deleting object in S3: {:?}", err);
118 ClientError::DeleteObject(err)
119 })?;
120
121 Ok(())
122 }
123}