ds_common_aws_rs_lib/s3/
mod.rs

1//! AWS S3 module.
2//!
3//! This module contains functions for interacting with AWS S3.
4
5pub mod error;
6
7use aws_sdk_s3::{primitives::ByteStream, Client};
8
9use crate::error::{Error, Result};
10use crate::s3::error::{map_s3_err, S3Error};
11
12/// S3 service for interacting with AWS S3
13///
14/// This service provides a high-level interface for S3 operations
15/// with dependency injection for better performance and testability.
16pub struct S3Service {
17    client: Client,
18}
19
20impl S3Service {
21    /// Create a new S3 service with a custom client
22    ///
23    /// # Arguments
24    ///
25    /// * `client` - The S3 client to use
26    ///
27    /// # Returns
28    ///
29    /// Returns a new S3Service instance with the provided client.
30    pub fn with_client(client: Client) -> Self {
31        Self { client }
32    }
33
34    /// Create a new S3 service with a shared configuration
35    ///
36    /// # Arguments
37    ///
38    /// * `config` - The AWS SDK configuration to use
39    ///
40    /// # Returns
41    ///
42    /// Returns a new S3Service instance with the provided configuration.
43    pub fn with_config(config: &aws_config::SdkConfig) -> Self {
44        Self {
45            client: Client::new(config),
46        }
47    }
48
49    /// Get an S3 object
50    ///
51    /// # Arguments
52    ///
53    /// * `bucket` - Name of the S3 bucket.
54    /// * `key` - Object key within the bucket.
55    ///
56    /// # Returns
57    ///
58    /// The object contents as a `Vec<u8>`.
59    ///
60    /// # Errors
61    ///
62    /// * [`S3Error::Service`] - If the S3 client fails to create.
63    /// * [`S3Error::Timeout`] - If the request times out.
64    /// * [`S3Error::Transport`] - If the request fails to dispatch.
65    /// * [`S3Error::Build`] - If the request fails to build.
66    pub async fn get_object(&self, bucket: &str, key: &str) -> Result<Vec<u8>> {
67        let response = self
68            .client
69            .get_object()
70            .bucket(bucket)
71            .key(key)
72            .send()
73            .await
74            .map_err(map_s3_err)?;
75
76        let data = response
77            .body
78            .collect()
79            .await
80            .map_err(|e| {
81                Error::from(S3Error::Service(Box::new(std::io::Error::other(format!(
82                    "Failed to collect S3 body: {}",
83                    e
84                )))))
85            })?
86            .into_bytes()
87            .to_vec();
88
89        Ok(data)
90    }
91
92    /// Put an S3 object
93    ///
94    /// # Arguments
95    ///
96    /// * `bucket` - The bucket of the object to put
97    /// * `key` - The key of the object to put
98    /// * `value` - The value of the object to put
99    /// * `content_type` - The content type of the object
100    ///
101    /// # Returns
102    ///
103    /// Returns `Ok(())` if the object is put successfully.
104    ///
105    /// # Errors
106    ///
107    /// * [`S3Error::Service`] - If the S3 client fails to create.
108    /// * [`S3Error::Timeout`] - If the request times out.
109    /// * [`S3Error::Transport`] - If the request fails to dispatch.
110    /// * [`S3Error::Build`] - If the request fails to build.
111    pub async fn put_object(
112        &self,
113        bucket: &str,
114        key: &str,
115        value: impl Into<ByteStream>,
116        content_type: Option<&str>,
117    ) -> Result<()> {
118        let mut req = self.client.put_object().bucket(bucket).key(key).body(value.into());
119
120        if let Some(ct) = content_type {
121            req = req.content_type(ct);
122        }
123
124        req.send().await.map_err(map_s3_err)?;
125        Ok(())
126    }
127}