this repo has no description
1use async_trait::async_trait;
2use aws_config::BehaviorVersion;
3use aws_config::meta::region::RegionProviderChain;
4use aws_sdk_s3::Client;
5use aws_sdk_s3::primitives::ByteStream;
6use thiserror::Error;
7
8#[derive(Error, Debug)]
9pub enum StorageError {
10 #[error("IO error: {0}")]
11 Io(#[from] std::io::Error),
12 #[error("S3 error: {0}")]
13 S3(String),
14 #[error("Other: {0}")]
15 Other(String),
16}
17
18#[async_trait]
19pub trait BlobStorage: Send + Sync {
20 async fn put(&self, key: &str, data: &[u8]) -> Result<(), StorageError>;
21 async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError>;
22 async fn delete(&self, key: &str) -> Result<(), StorageError>;
23}
24
25pub struct S3BlobStorage {
26 client: Client,
27 bucket: String,
28}
29
30impl S3BlobStorage {
31 pub async fn new() -> Self {
32 // heheheh
33 let region_provider = RegionProviderChain::default_provider().or_else("us-east-1");
34 let config = aws_config::defaults(BehaviorVersion::latest())
35 .region(region_provider)
36 .load()
37 .await;
38
39 let bucket = std::env::var("S3_BUCKET").expect("S3_BUCKET must be set");
40
41 let client = if let Ok(endpoint) = std::env::var("S3_ENDPOINT") {
42 let s3_config = aws_sdk_s3::config::Builder::from(&config)
43 .endpoint_url(endpoint)
44 .force_path_style(true)
45 .build();
46 Client::from_conf(s3_config)
47 } else {
48 Client::new(&config)
49 };
50
51 Self { client, bucket }
52 }
53}
54
55#[async_trait]
56impl BlobStorage for S3BlobStorage {
57 async fn put(&self, key: &str, data: &[u8]) -> Result<(), StorageError> {
58 self.client
59 .put_object()
60 .bucket(&self.bucket)
61 .key(key)
62 .body(ByteStream::from(data.to_vec()))
63 .send()
64 .await
65 .map_err(|e| StorageError::S3(e.to_string()))?;
66 Ok(())
67 }
68
69 async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError> {
70 let resp = self
71 .client
72 .get_object()
73 .bucket(&self.bucket)
74 .key(key)
75 .send()
76 .await
77 .map_err(|e| StorageError::S3(e.to_string()))?;
78
79 let data = resp
80 .body
81 .collect()
82 .await
83 .map_err(|e| StorageError::S3(e.to_string()))?
84 .into_bytes();
85
86 Ok(data.to_vec())
87 }
88
89 async fn delete(&self, key: &str) -> Result<(), StorageError> {
90 self.client
91 .delete_object()
92 .bucket(&self.bucket)
93 .key(key)
94 .send()
95 .await
96 .map_err(|e| StorageError::S3(e.to_string()))?;
97 Ok(())
98 }
99}