rustack_s3_core/
config.rs1use serde::{Deserialize, Serialize};
8use typed_builder::TypedBuilder;
9
10#[derive(Debug, Clone, Serialize, Deserialize, TypedBuilder)]
25#[serde(rename_all = "camelCase")]
26pub struct S3Config {
27 #[builder(default = String::from("0.0.0.0:4566"))]
29 pub gateway_listen: String,
30
31 #[builder(default = true)]
33 pub s3_virtual_hosting: bool,
34
35 #[builder(default = String::from("s3.localhost.localstack.cloud"))]
37 pub s3_domain: String,
38
39 #[builder(default = true)]
41 pub s3_skip_signature_validation: bool,
42
43 #[builder(default = 524_288)]
45 pub s3_max_memory_object_size: usize,
46
47 #[builder(default = String::from("us-east-1"))]
49 pub default_region: String,
50
51 #[builder(default = String::from("info"))]
53 pub log_level: String,
54
55 #[builder(default = false)]
57 pub persistence: bool,
58
59 #[builder(default = String::from("/var/lib/localstack"))]
61 pub data_dir: String,
62}
63
64impl Default for S3Config {
65 fn default() -> Self {
66 Self {
67 gateway_listen: String::from("0.0.0.0:4566"),
68 s3_virtual_hosting: true,
69 s3_domain: String::from("s3.localhost.localstack.cloud"),
70 s3_skip_signature_validation: true,
71 s3_max_memory_object_size: 524_288,
72 default_region: String::from("us-east-1"),
73 log_level: String::from("info"),
74 persistence: false,
75 data_dir: String::from("/var/lib/localstack"),
76 }
77 }
78}
79
80impl S3Config {
81 #[must_use]
106 pub fn from_env() -> Self {
107 let mut config = Self::default();
108
109 if let Ok(v) = std::env::var("GATEWAY_LISTEN") {
110 config.gateway_listen = v;
111 }
112 if let Ok(v) = std::env::var("S3_VIRTUAL_HOSTING") {
113 config.s3_virtual_hosting = parse_bool(&v);
114 }
115 if let Ok(v) = std::env::var("S3_DOMAIN") {
116 config.s3_domain = v;
117 }
118 if let Ok(v) = std::env::var("S3_SKIP_SIGNATURE_VALIDATION") {
119 config.s3_skip_signature_validation = parse_bool(&v);
120 }
121 if let Ok(v) = std::env::var("S3_MAX_MEMORY_OBJECT_SIZE") {
122 if let Ok(n) = v.parse::<usize>() {
123 config.s3_max_memory_object_size = n;
124 }
125 }
126 if let Ok(v) = std::env::var("DEFAULT_REGION") {
127 config.default_region = v;
128 }
129 if let Ok(v) = std::env::var("LOG_LEVEL") {
130 config.log_level = v;
131 }
132 if let Ok(v) = std::env::var("PERSISTENCE") {
133 config.persistence = parse_bool(&v);
134 }
135 if let Ok(v) = std::env::var("DATA_DIR") {
136 config.data_dir = v;
137 }
138
139 config
140 }
141}
142
143fn parse_bool(value: &str) -> bool {
145 value == "1" || value.eq_ignore_ascii_case("true")
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_should_create_default_config() {
154 let config = S3Config::default();
155 assert_eq!(config.gateway_listen, "0.0.0.0:4566");
156 assert!(config.s3_virtual_hosting);
157 assert_eq!(config.s3_domain, "s3.localhost.localstack.cloud");
158 assert!(config.s3_skip_signature_validation);
159 assert_eq!(config.s3_max_memory_object_size, 524_288);
160 assert_eq!(config.default_region, "us-east-1");
161 assert_eq!(config.log_level, "info");
162 assert!(!config.persistence);
163 assert_eq!(config.data_dir, "/var/lib/localstack");
164 }
165
166 #[test]
167 fn test_should_load_from_env() {
168 let config = S3Config::from_env();
169 assert!(!config.gateway_listen.is_empty());
170 }
171
172 #[test]
173 fn test_should_build_with_typed_builder() {
174 let config = S3Config::builder()
175 .gateway_listen("127.0.0.1:9999".into())
176 .s3_virtual_hosting(false)
177 .s3_domain("custom.domain".into())
178 .s3_skip_signature_validation(false)
179 .s3_max_memory_object_size(1024)
180 .default_region("eu-west-1".into())
181 .log_level("debug".into())
182 .persistence(true)
183 .data_dir("/tmp/data".into())
184 .build();
185
186 assert_eq!(config.gateway_listen, "127.0.0.1:9999");
187 assert!(!config.s3_virtual_hosting);
188 assert_eq!(config.s3_domain, "custom.domain");
189 assert!(!config.s3_skip_signature_validation);
190 assert_eq!(config.s3_max_memory_object_size, 1024);
191 assert_eq!(config.default_region, "eu-west-1");
192 assert_eq!(config.log_level, "debug");
193 assert!(config.persistence);
194 assert_eq!(config.data_dir, "/tmp/data");
195 }
196
197 #[test]
198 fn test_should_serialize_to_camel_case_json() {
199 let config = S3Config::default();
200 let json = serde_json::to_string(&config).expect("test serialization");
201 assert!(json.contains("gatewayListen"));
202 assert!(json.contains("s3VirtualHosting"));
203 }
204
205 #[test]
206 fn test_should_parse_bool_values() {
207 assert!(parse_bool("1"));
208 assert!(parse_bool("true"));
209 assert!(parse_bool("TRUE"));
210 assert!(parse_bool("True"));
211 assert!(!parse_bool("0"));
212 assert!(!parse_bool("false"));
213 assert!(!parse_bool(""));
214 }
215}