1use aws_config::AppName;
23use aws_credential_types::{provider::SharedCredentialsProvider, Credentials};
24use aws_sdk_s3::{
25 config::Region,
26 types::{BucketCannedAcl, ObjectCannedAcl},
27};
28
29#[derive(Debug, Clone, Default)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub struct StorageConfig {
33 #[cfg_attr(feature = "serde", serde(default))]
36 pub enable_signer_v4_requests: bool,
37
38 #[cfg_attr(feature = "serde", serde(default))]
44 pub enforce_path_access_style: bool,
45
46 #[cfg_attr(
48 feature = "serde",
49 serde(default, with = "__serde::object_acl", skip_serializing_if = "Option::is_none")
50 )]
51 pub default_object_acl: Option<ObjectCannedAcl>,
52
53 #[cfg_attr(
56 feature = "serde",
57 serde(default, with = "__serde::bucket_acl", skip_serializing_if = "Option::is_none")
58 )]
59 pub default_bucket_acl: Option<BucketCannedAcl>,
60
61 pub secret_access_key: String,
63
64 pub access_key_id: String,
66
67 #[cfg_attr(feature = "serde", serde(default))]
69 pub app_name: Option<String>,
70
71 #[cfg_attr(feature = "serde", serde(default))]
73 pub endpoint: Option<String>,
74
75 #[cfg_attr(feature = "serde", serde(default))]
77 pub prefix: Option<String>,
78
79 #[cfg_attr(
81 feature = "serde",
82 serde(default, with = "__serde::region", skip_serializing_if = "Option::is_none")
83 )]
84 pub region: Option<Region>,
85
86 pub bucket: String,
88}
89
90impl From<StorageConfig> for aws_sdk_s3::Config {
91 fn from(config: StorageConfig) -> aws_sdk_s3::Config {
92 let mut cfg = aws_sdk_s3::Config::builder();
93 cfg.set_credentials_provider(Some(SharedCredentialsProvider::new(Credentials::new(
94 &config.access_key_id,
95 &config.secret_access_key,
96 None,
97 None,
98 "remi-rs",
99 ))))
100 .set_endpoint_url(config.endpoint.clone())
101 .set_app_name(Some(
102 AppName::new(config.app_name.clone().unwrap_or(String::from("remi-rs"))).unwrap(),
103 ));
104
105 if config.enforce_path_access_style {
106 cfg.set_force_path_style(Some(true));
107 }
108
109 cfg.region(config.region).build()
110 }
111}
112
113#[cfg(feature = "serde")]
115mod __serde {
116 pub mod region {
117 use aws_sdk_s3::config::Region;
118 use serde::{de::Deserializer, ser::Serializer, Deserialize};
119 use std::borrow::Cow;
120
121 pub fn serialize<S: Serializer>(region: &Option<Region>, serializer: S) -> Result<S::Ok, S::Error> {
122 match region {
123 Some(region) => serializer.serialize_str(region.as_ref()),
124 None => unreachable!(), }
126 }
127
128 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Region>, D::Error>
129 where
130 D: Deserializer<'de>,
131 {
132 let s = String::deserialize(deserializer)?;
133 Ok(Some(Region::new(Cow::Owned(s))))
134 }
135 }
136
137 pub mod bucket_acl {
138 use aws_sdk_s3::types::BucketCannedAcl;
139 use serde::*;
140
141 pub fn serialize<S: Serializer>(acl: &Option<BucketCannedAcl>, serializer: S) -> Result<S::Ok, S::Error> {
142 match acl {
143 Some(acl) => serializer.serialize_str(acl.as_str()),
144 None => unreachable!(),
145 }
146 }
147
148 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<BucketCannedAcl>, D::Error>
149 where
150 D: Deserializer<'de>,
151 {
152 let s = String::deserialize(deserializer)?;
153 Ok(Some(s.as_str().into()))
154 }
155 }
156
157 pub mod object_acl {
158 use aws_sdk_s3::types::ObjectCannedAcl;
159 use serde::*;
160
161 pub fn serialize<S: Serializer>(acl: &Option<ObjectCannedAcl>, serializer: S) -> Result<S::Ok, S::Error> {
162 match acl {
163 Some(acl) => serializer.serialize_str(acl.as_str()),
164 None => unreachable!(),
165 }
166 }
167
168 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<ObjectCannedAcl>, D::Error>
169 where
170 D: Deserializer<'de>,
171 {
172 let s = String::deserialize(deserializer)?;
173 Ok(Some(s.as_str().into()))
174 }
175 }
176}