garage_sdk/storage/
client.rs

1use aws_sdk_s3::{Client, primitives::ByteStream};
2use std::error::Error as StdError;
3use std::future::Future;
4
5/// Input payload for storage uploads.
6#[derive(Debug)]
7pub struct StorageInput {
8    /// Object key in the bucket.
9    pub key: String,
10    /// Content type for the object.
11    pub content_type: String,
12    /// Object body.
13    pub body: ByteStream,
14}
15
16/// Storage upload result.
17#[derive(Debug)]
18pub struct StorageResult {
19    /// ETag returned by storage.
20    pub etag: Option<String>,
21}
22
23/// Storage client abstraction for uploading content.
24pub trait StorageClient {
25    /// Error type for storage operations.
26    type Error: StdError + Send + Sync + 'static;
27    /// Future returned by storage operations.
28    type Future<'a>: Future<Output = std::result::Result<StorageResult, Self::Error>> + Send
29    where
30        Self: 'a;
31
32    /// Upload an object to storage.
33    fn put_object<'a>(&'a self, input: StorageInput) -> Self::Future<'a>;
34}
35
36/// Default storage client using aws-sdk-s3.
37#[derive(Clone, Debug)]
38pub struct S3Storage {
39    client: Client,
40    bucket: String,
41}
42
43impl S3Storage {
44    /// Create a new S3 storage client.
45    pub fn new(client: Client, bucket: impl Into<String>) -> Self {
46        Self {
47            client,
48            bucket: bucket.into(),
49        }
50    }
51}
52
53impl StorageClient for S3Storage {
54    type Error = aws_sdk_s3::Error;
55    type Future<'a> = std::pin::Pin<
56        Box<dyn Future<Output = std::result::Result<StorageResult, Self::Error>> + Send + 'a>,
57    >;
58
59    fn put_object<'a>(&'a self, input: StorageInput) -> Self::Future<'a> {
60        Box::pin(async move {
61            let result = self
62                .client
63                .put_object()
64                .bucket(&self.bucket)
65                .key(&input.key)
66                .content_type(&input.content_type)
67                .body(input.body)
68                .send()
69                .await?;
70
71            Ok(StorageResult {
72                etag: result.e_tag().map(|s| s.trim_matches('"').to_string()),
73            })
74        })
75    }
76}