1use crate::aliyun::AliyunOssStorage;
2use crate::error::StorageError;
3use crate::huawei::HuaweiObsStorage;
4use crate::local::LocalStorage;
5use crate::qiniu::QiniuKodoStorage;
6use crate::s3::S3Storage;
7use crate::tencent::TencentCosStorage;
8use crate::upyun::UpYunStorage;
9use async_trait::async_trait;
10
11#[async_trait]
12pub trait Storage: Send + Sync {
13 async fn put(&self, key: &str, data: &[u8], content_type: &str)
14 -> Result<String, StorageError>;
15 async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError>;
16 async fn delete(&self, key: &str) -> Result<(), StorageError>;
17 async fn exists(&self, key: &str) -> Result<bool, StorageError>;
18}
19
20pub struct StorageBuilder {
21 provider: StorageProvider,
22 config: StorageConfig,
23}
24
25impl StorageBuilder {
26 pub fn new(provider: StorageProvider) -> Self {
27 Self {
28 provider,
29 config: StorageConfig::default(),
30 }
31 }
32
33 pub fn with_bucket(mut self, bucket: impl Into<String>) -> Self {
34 self.config.bucket = bucket.into();
35 self
36 }
37
38 pub fn with_region(mut self, region: impl Into<String>) -> Self {
39 self.config.region = region.into();
40 self
41 }
42
43 pub fn with_endpoint(mut self, endpoint: impl Into<String>) -> Self {
44 self.config.endpoint = Some(endpoint.into());
45 self
46 }
47
48 pub fn with_access_key(mut self, key: impl Into<String>) -> Self {
49 self.config.access_key = Some(key.into());
50 self
51 }
52
53 pub fn with_secret_key(mut self, key: impl Into<String>) -> Self {
54 self.config.secret_key = Some(key.into());
55 self
56 }
57
58 pub fn with_path_prefix(mut self, prefix: impl Into<String>) -> Self {
59 self.config.path_prefix = Some(prefix.into());
60 self
61 }
62
63 pub fn with_base_path(mut self, base_path: impl Into<String>) -> Self {
64 self.config.base_path = Some(base_path.into());
65 self
66 }
67
68 pub fn build(self) -> Result<StorageWrapper, StorageError> {
69 match self.provider {
70 StorageProvider::Local => {
71 let base_path = self
72 .config
73 .base_path
74 .clone()
75 .unwrap_or_else(|| ".".to_string());
76 Ok(StorageWrapper::Local(LocalStorage::new(base_path)))
77 }
78 StorageProvider::S3(_) => {
79 let bucket = self.config.bucket.clone();
80 let region = self.config.region.clone();
81 Ok(StorageWrapper::S3(S3Storage::new(bucket, region)))
82 }
83 StorageProvider::AliyunOss(_) => {
84 let bucket = self.config.bucket.clone();
85 let endpoint = self.config.endpoint.clone().unwrap_or_default();
86 Ok(StorageWrapper::Aliyun(AliyunOssStorage::new(
87 bucket, endpoint,
88 )))
89 }
90 StorageProvider::TencentCos(_) => {
91 let bucket = self.config.bucket.clone();
92 let region = self.config.region.clone();
93 Ok(StorageWrapper::Tencent(TencentCosStorage::new(
94 bucket, region,
95 )))
96 }
97 StorageProvider::QiniuKodo(_) => {
98 let bucket = self.config.bucket.clone();
99 Ok(StorageWrapper::Qiniu(QiniuKodoStorage::new(bucket)))
100 }
101 StorageProvider::HuaweiObs(_) => {
102 let bucket = self.config.bucket.clone();
103 let endpoint = self.config.endpoint.clone().unwrap_or_default();
104 Ok(StorageWrapper::Huawei(HuaweiObsStorage::new(
105 bucket, endpoint,
106 )))
107 }
108 StorageProvider::UpYun(_) => {
109 let bucket = self.config.bucket.clone();
110 Ok(StorageWrapper::Upyun(UpYunStorage::new(bucket)))
111 }
112 }
113 }
114}
115
116#[derive(Clone)]
117pub struct StorageConfig {
118 pub bucket: String,
119 pub region: String,
120 pub endpoint: Option<String>,
121 pub access_key: Option<String>,
122 pub secret_key: Option<String>,
123 pub path_prefix: Option<String>,
124 pub base_path: Option<String>,
125}
126
127impl std::fmt::Debug for StorageConfig {
128 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
129 f.debug_struct("StorageConfig")
130 .field("bucket", &self.bucket)
131 .field("region", &self.region)
132 .field("endpoint", &self.endpoint)
133 .field("access_key", &"***")
134 .field("secret_key", &"***")
135 .field("path_prefix", &self.path_prefix)
136 .field("base_path", &self.base_path)
137 .finish()
138 }
139}
140
141impl Default for StorageConfig {
142 fn default() -> Self {
143 Self {
144 bucket: "default-bucket".to_string(),
145 region: "us-east-1".to_string(),
146 endpoint: None,
147 access_key: None,
148 secret_key: None,
149 path_prefix: None,
150 base_path: None,
151 }
152 }
153}
154
155#[derive(Debug, Clone)]
156pub enum StorageProvider {
157 Local,
158 S3(S3Config),
159 AliyunOss(AliyunConfig),
160 TencentCos(TencentConfig),
161 QiniuKodo(QiniuConfig),
162 HuaweiObs(HuaweiConfig),
163 UpYun(UpYunConfig),
164}
165
166#[derive(Debug, Clone, Default)]
167pub struct S3Config {
168 pub region: String,
169 pub access_key: Option<String>,
170 pub secret_key: Option<String>,
171}
172
173#[derive(Debug, Clone, Default)]
174pub struct AliyunConfig {
175 pub endpoint: String,
176 pub access_key: Option<String>,
177 pub secret_key: Option<String>,
178}
179
180#[derive(Debug, Clone, Default)]
181pub struct TencentConfig {
182 pub region: String,
183 pub secret_id: Option<String>,
184 pub secret_key: Option<String>,
185}
186
187#[derive(Debug, Clone, Default)]
188pub struct QiniuConfig {
189 pub access_key: Option<String>,
190 pub secret_key: Option<String>,
191}
192
193#[derive(Debug, Clone, Default)]
194pub struct HuaweiConfig {
195 pub endpoint: String,
196 pub access_key: Option<String>,
197 pub secret_key: Option<String>,
198}
199
200#[derive(Debug, Clone, Default)]
201pub struct UpYunConfig {
202 pub operator: Option<String>,
203 pub password: Option<String>,
204}
205
206pub enum StorageWrapper {
207 Local(LocalStorage),
208 S3(S3Storage),
209 Aliyun(AliyunOssStorage),
210 Tencent(TencentCosStorage),
211 Qiniu(QiniuKodoStorage),
212 Huawei(HuaweiObsStorage),
213 Upyun(UpYunStorage),
214}
215
216#[async_trait]
217impl Storage for StorageWrapper {
218 async fn put(
219 &self,
220 key: &str,
221 data: &[u8],
222 content_type: &str,
223 ) -> Result<String, StorageError> {
224 match self {
225 StorageWrapper::Local(s) => s.put(key, data, content_type).await,
226 StorageWrapper::S3(s) => s.put(key, data, content_type).await,
227 StorageWrapper::Aliyun(s) => s.put(key, data, content_type).await,
228 StorageWrapper::Tencent(s) => s.put(key, data, content_type).await,
229 StorageWrapper::Qiniu(s) => s.put(key, data, content_type).await,
230 StorageWrapper::Huawei(s) => s.put(key, data, content_type).await,
231 StorageWrapper::Upyun(s) => s.put(key, data, content_type).await,
232 }
233 }
234
235 async fn get(&self, key: &str) -> Result<Vec<u8>, StorageError> {
236 match self {
237 StorageWrapper::Local(s) => s.get(key).await,
238 StorageWrapper::S3(s) => s.get(key).await,
239 StorageWrapper::Aliyun(s) => s.get(key).await,
240 StorageWrapper::Tencent(s) => s.get(key).await,
241 StorageWrapper::Qiniu(s) => s.get(key).await,
242 StorageWrapper::Huawei(s) => s.get(key).await,
243 StorageWrapper::Upyun(s) => s.get(key).await,
244 }
245 }
246
247 async fn delete(&self, key: &str) -> Result<(), StorageError> {
248 match self {
249 StorageWrapper::Local(s) => s.delete(key).await,
250 StorageWrapper::S3(s) => s.delete(key).await,
251 StorageWrapper::Aliyun(s) => s.delete(key).await,
252 StorageWrapper::Tencent(s) => s.delete(key).await,
253 StorageWrapper::Qiniu(s) => s.delete(key).await,
254 StorageWrapper::Huawei(s) => s.delete(key).await,
255 StorageWrapper::Upyun(s) => s.delete(key).await,
256 }
257 }
258
259 async fn exists(&self, key: &str) -> Result<bool, StorageError> {
260 match self {
261 StorageWrapper::Local(s) => s.exists(key).await,
262 StorageWrapper::S3(s) => s.exists(key).await,
263 StorageWrapper::Aliyun(s) => s.exists(key).await,
264 StorageWrapper::Tencent(s) => s.exists(key).await,
265 StorageWrapper::Qiniu(s) => s.exists(key).await,
266 StorageWrapper::Huawei(s) => s.exists(key).await,
267 StorageWrapper::Upyun(s) => s.exists(key).await,
268 }
269 }
270}