1use std::time::Duration;
16
17use common_base::readable_size::ReadableSize;
18use common_base::secrets::{ExposeSecret, SecretString};
19use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
23#[serde(tag = "type")]
24pub enum ObjectStoreConfig {
25 File(FileConfig),
26 S3(S3Config),
27 Oss(OssConfig),
28 Azblob(AzblobConfig),
29 Gcs(GcsConfig),
30}
31
32impl Default for ObjectStoreConfig {
33 fn default() -> Self {
34 ObjectStoreConfig::File(FileConfig {})
35 }
36}
37
38impl ObjectStoreConfig {
39 pub fn provider_name(&self) -> &'static str {
41 match self {
42 Self::File(_) => "File",
43 Self::S3(_) => "S3",
44 Self::Oss(_) => "Oss",
45 Self::Azblob(_) => "Azblob",
46 Self::Gcs(_) => "Gcs",
47 }
48 }
49
50 pub fn is_object_storage(&self) -> bool {
52 !matches!(self, Self::File(_))
53 }
54
55 pub fn config_name(&self) -> &str {
57 let name = match self {
58 Self::File(_) => self.provider_name(),
60 Self::S3(s3) => &s3.name,
61 Self::Oss(oss) => &oss.name,
62 Self::Azblob(az) => &az.name,
63 Self::Gcs(gcs) => &gcs.name,
64 };
65
66 if name.trim().is_empty() {
67 return self.provider_name();
68 }
69
70 name
71 }
72}
73
74#[derive(Debug, Clone, Serialize, Default, Deserialize, Eq, PartialEq)]
75#[serde(default)]
76pub struct FileConfig {}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
79#[serde(default)]
80pub struct S3Config {
81 pub name: String,
82 pub bucket: String,
83 pub root: String,
84 #[serde(skip_serializing)]
85 pub access_key_id: SecretString,
86 #[serde(skip_serializing)]
87 pub secret_access_key: SecretString,
88 pub endpoint: Option<String>,
89 pub region: Option<String>,
90 pub enable_virtual_host_style: bool,
94 #[serde(flatten)]
95 pub cache: ObjectStorageCacheConfig,
96 pub http_client: HttpClientConfig,
97}
98
99impl Default for S3Config {
100 fn default() -> Self {
101 Self {
102 name: String::default(),
103 bucket: String::default(),
104 root: String::default(),
105 access_key_id: SecretString::from(String::default()),
106 secret_access_key: SecretString::from(String::default()),
107 enable_virtual_host_style: false,
108 endpoint: Option::default(),
109 region: Option::default(),
110 cache: ObjectStorageCacheConfig::default(),
111 http_client: HttpClientConfig::default(),
112 }
113 }
114}
115
116impl PartialEq for S3Config {
117 fn eq(&self, other: &Self) -> bool {
118 self.name == other.name
119 && self.bucket == other.bucket
120 && self.root == other.root
121 && self.access_key_id.expose_secret() == other.access_key_id.expose_secret()
122 && self.secret_access_key.expose_secret() == other.secret_access_key.expose_secret()
123 && self.endpoint == other.endpoint
124 && self.region == other.region
125 && self.enable_virtual_host_style == other.enable_virtual_host_style
126 && self.cache == other.cache
127 && self.http_client == other.http_client
128 }
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(default)]
133pub struct OssConfig {
134 pub name: String,
135 pub bucket: String,
136 pub root: String,
137 #[serde(skip_serializing)]
138 pub access_key_id: SecretString,
139 #[serde(skip_serializing)]
140 pub access_key_secret: SecretString,
141 pub endpoint: String,
142 #[serde(flatten)]
143 pub cache: ObjectStorageCacheConfig,
144 pub http_client: HttpClientConfig,
145}
146
147impl PartialEq for OssConfig {
148 fn eq(&self, other: &Self) -> bool {
149 self.name == other.name
150 && self.bucket == other.bucket
151 && self.root == other.root
152 && self.access_key_id.expose_secret() == other.access_key_id.expose_secret()
153 && self.access_key_secret.expose_secret() == other.access_key_secret.expose_secret()
154 && self.endpoint == other.endpoint
155 && self.cache == other.cache
156 && self.http_client == other.http_client
157 }
158}
159
160impl Default for OssConfig {
161 fn default() -> Self {
162 Self {
163 name: String::default(),
164 bucket: String::default(),
165 root: String::default(),
166 access_key_id: SecretString::from(String::default()),
167 access_key_secret: SecretString::from(String::default()),
168 endpoint: String::default(),
169 cache: ObjectStorageCacheConfig::default(),
170 http_client: HttpClientConfig::default(),
171 }
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
176#[serde(default)]
177pub struct AzblobConfig {
178 pub name: String,
179 pub container: String,
180 pub root: String,
181 #[serde(skip_serializing)]
182 pub account_name: SecretString,
183 #[serde(skip_serializing)]
184 pub account_key: SecretString,
185 pub endpoint: String,
186 pub sas_token: Option<String>,
187 #[serde(flatten)]
188 pub cache: ObjectStorageCacheConfig,
189 pub http_client: HttpClientConfig,
190}
191
192impl PartialEq for AzblobConfig {
193 fn eq(&self, other: &Self) -> bool {
194 self.name == other.name
195 && self.container == other.container
196 && self.root == other.root
197 && self.account_name.expose_secret() == other.account_name.expose_secret()
198 && self.account_key.expose_secret() == other.account_key.expose_secret()
199 && self.endpoint == other.endpoint
200 && self.sas_token == other.sas_token
201 && self.cache == other.cache
202 && self.http_client == other.http_client
203 }
204}
205impl Default for AzblobConfig {
206 fn default() -> Self {
207 Self {
208 name: String::default(),
209 container: String::default(),
210 root: String::default(),
211 account_name: SecretString::from(String::default()),
212 account_key: SecretString::from(String::default()),
213 endpoint: String::default(),
214 sas_token: Option::default(),
215 cache: ObjectStorageCacheConfig::default(),
216 http_client: HttpClientConfig::default(),
217 }
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
222#[serde(default)]
223pub struct GcsConfig {
224 pub name: String,
225 pub root: String,
226 pub bucket: String,
227 pub scope: String,
228 #[serde(skip_serializing)]
229 pub credential_path: SecretString,
230 #[serde(skip_serializing)]
231 pub credential: SecretString,
232 pub endpoint: String,
233 #[serde(flatten)]
234 pub cache: ObjectStorageCacheConfig,
235 pub http_client: HttpClientConfig,
236}
237
238impl Default for GcsConfig {
239 fn default() -> Self {
240 Self {
241 name: String::default(),
242 root: String::default(),
243 bucket: String::default(),
244 scope: String::default(),
245 credential_path: SecretString::from(String::default()),
246 credential: SecretString::from(String::default()),
247 endpoint: String::default(),
248 cache: ObjectStorageCacheConfig::default(),
249 http_client: HttpClientConfig::default(),
250 }
251 }
252}
253
254impl PartialEq for GcsConfig {
255 fn eq(&self, other: &Self) -> bool {
256 self.name == other.name
257 && self.root == other.root
258 && self.bucket == other.bucket
259 && self.scope == other.scope
260 && self.credential_path.expose_secret() == other.credential_path.expose_secret()
261 && self.credential.expose_secret() == other.credential.expose_secret()
262 && self.endpoint == other.endpoint
263 && self.cache == other.cache
264 && self.http_client == other.http_client
265 }
266}
267
268#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
270#[serde(default)]
271pub struct HttpClientConfig {
272 pub(crate) pool_max_idle_per_host: u32,
274
275 #[serde(with = "humantime_serde")]
277 pub(crate) connect_timeout: Duration,
278
279 #[serde(with = "humantime_serde")]
282 pub(crate) timeout: Duration,
283
284 #[serde(with = "humantime_serde")]
286 pub(crate) pool_idle_timeout: Duration,
287
288 pub skip_ssl_validation: bool,
290}
291
292impl Default for HttpClientConfig {
293 fn default() -> Self {
294 Self {
295 pool_max_idle_per_host: 1024,
296 connect_timeout: Duration::from_secs(30),
297 timeout: Duration::from_secs(30),
298 pool_idle_timeout: Duration::from_secs(90),
299 skip_ssl_validation: false,
300 }
301 }
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
305#[serde(default)]
306pub struct ObjectStorageCacheConfig {
307 pub cache_path: Option<String>,
309 pub cache_capacity: Option<ReadableSize>,
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316 use crate::config::ObjectStoreConfig;
317
318 #[test]
319 fn test_config_name() {
320 let object_store_config = ObjectStoreConfig::default();
321 assert_eq!("File", object_store_config.config_name());
322
323 let s3_config = ObjectStoreConfig::S3(S3Config::default());
324 assert_eq!("S3", s3_config.config_name());
325 assert_eq!("S3", s3_config.provider_name());
326
327 let s3_config = ObjectStoreConfig::S3(S3Config {
328 name: "test".to_string(),
329 ..Default::default()
330 });
331 assert_eq!("test", s3_config.config_name());
332 assert_eq!("S3", s3_config.provider_name());
333 }
334
335 #[test]
336 fn test_is_object_storage() {
337 let store = ObjectStoreConfig::default();
338 assert!(!store.is_object_storage());
339 let s3_config = ObjectStoreConfig::S3(S3Config::default());
340 assert!(s3_config.is_object_storage());
341 let oss_config = ObjectStoreConfig::Oss(OssConfig::default());
342 assert!(oss_config.is_object_storage());
343 let gcs_config = ObjectStoreConfig::Gcs(GcsConfig::default());
344 assert!(gcs_config.is_object_storage());
345 let azblob_config = ObjectStoreConfig::Azblob(AzblobConfig::default());
346 assert!(azblob_config.is_object_storage());
347 }
348}