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}