1#![doc(html_logo_url = "https://cdn.floofy.dev/images/trans.png")]
67#![cfg_attr(any(noeldoc, docsrs), feature(doc_cfg))]
68#![allow(non_camel_case_types)]
69
70use remi::{ListBlobsRequest, UploadRequest};
71use std::{borrow::Cow, path::Path};
72
73pub use remi as core;
74
75#[cfg(feature = "gridfs")]
76#[cfg_attr(any(docsrs, noeldoc), doc(cfg(feature = "gridfs")))]
77pub use remi_gridfs as gridfs;
78
79#[cfg(feature = "azure")]
80#[cfg_attr(any(docsrs, noeldoc), doc(cfg(feature = "azure")))]
81pub use remi_azure as azure;
82
83#[cfg(feature = "s3")]
84#[cfg_attr(any(docsrs, noeldoc), doc(cfg(feature = "s3")))]
85pub use remi_s3 as s3;
86
87#[cfg(feature = "fs")]
88#[cfg_attr(any(docsrs, noeldoc), doc(cfg(feature = "fs")))]
89pub use remi_fs as fs;
90
91macro_rules! mk_storage_service_impl {
92 (
93 $(#[$meta:meta])*
94 $($feat:literal => $field:ident as $ty:ty {
95 $(#[$error_meta:meta])*
96 Error: $error:ty;
97
98 $(#[$config_meta:meta])*
99 Config: $config:ty;
100
101 Display: |$f:ident, $error_name:ident| $display:expr;
102 })*
103 ) => {
104 $(#[$meta])*
105 pub enum StorageService {
106 $(
107 #[cfg(feature = $feat)]
108 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = $feat)))]
109 $field($ty),
110 )*
111
112 __non_exhaustive
113 }
114
115 #[derive(Debug)]
117 #[allow(non_camel_case_types)]
118 pub enum Error {
119 $(
120 #[cfg(feature = $feat)]
121 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = $feat)))]
122 $(#[$error_meta])*
123 $field($error),
124 )*
125
126 __non_exhaustive,
127 }
128
129 impl ::std::fmt::Display for Error {
130 #[allow(unused)]
131 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
132 match self {
133 $(
134 #[cfg(feature = $feat)]
135 Error::$field(err) => {
136 let $error_name = err;
137 let $f = f;
138
139 $display
140 },
141 )*
142
143 _ => unreachable!()
144 }
145 }
146 }
147
148 impl ::std::error::Error for Error {
149 fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> {
150 match self {
151 $(
152 #[cfg(feature = $feat)]
153 Error::$field(err) => Some(err),
154 )*
155
156 _ => None
157 }
158 }
159 }
160
161 $(
162 #[cfg(feature = $feat)]
163 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = $feat)))]
164 impl ::core::convert::From<$error> for Error {
165 fn from(value: $error) -> Self {
166 Error::$field(value)
167 }
168 }
169 )*
170
171 #[derive(Debug, Clone, Default)]
172 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
173 #[cfg_attr(feature = "serde", serde(rename_all = "lowercase"))]
174 #[non_exhaustive]
175 pub enum Config {
176 $(
177 #[cfg(feature = $feat)]
178 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = $feat)))]
179 #[doc = concat!("Configuration variant that can be used to configure the [`StorageService::", stringify!($field), "`] variant.")]
180 $(#[$config_meta])*
181 $field($config),
182 )*
183
184 #[default]
185 Unknown,
186 }
187 };
188}
189
190mk_storage_service_impl! {
191 #[cfg_attr(feature = "gridfs", doc = "* [`remi_gridfs::StorageService`]")]
195 #[cfg_attr(feature = "azure", doc = "* [`remi_azure::StorageService`]")]
196 #[cfg_attr(feature = "fs", doc = "* [`remi_fs::StorageService`]")]
197 #[cfg_attr(feature = "s3", doc = "* [`remi_s3::StorageService`]")]
198 #[allow(non_camel_case_types)]
199 #[derive(Clone)]
200
201 "gridfs" => Gridfs as ::remi_gridfs::StorageService {
202 Error: ::remi_gridfs::mongodb::error::Error;
204 Config: ::remi_gridfs::StorageConfig;
205 Display: |f, err| match &*err.kind {
206 ::remi_gridfs::mongodb::error::ErrorKind::Custom(msg) => {
207 if let Some(msg) = msg.downcast_ref::<&str>() {
208 f.write_str(msg)
209 } else if let Some(msg) = msg.downcast_ref::<String>() {
210 f.write_str(msg)
211 } else {
212 ::std::fmt::Display::fmt(err, f)
213 }
214 },
215
216 _ => ::std::fmt::Display::fmt(err, f),
217 };
218 }
219
220 "azure" => Azure as ::remi_azure::StorageService {
221 Error: ::remi_azure::core::storage::Error;
223 Config: ::remi_azure::StorageConfig;
224 Display: |f, err| ::std::fmt::Display::fmt(err, f);
225 }
226
227 "fs" => Filesystem as ::remi_fs::StorageService {
228 Error: ::std::io::Error;
230 Config: ::remi_fs::StorageConfig;
231 Display: |f, err| ::std::fmt::Display::fmt(err, f);
232 }
233
234 "s3" => S3 as ::remi_s3::StorageService {
235 Error: ::remi_s3::Error;
237 Config: ::remi_s3::StorageConfig;
238 Display: |f, err| ::std::fmt::Display::fmt(err, f);
239 }
240}
241
242impl StorageService {
243 #[cfg(feature = "fs")]
244 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = "fs")))]
245 pub fn as_filesystem(&self) -> Option<&remi_fs::StorageService> {
248 match *self {
249 Self::Filesystem(ref fs) => Some(fs),
250 _ => None,
251 }
252 }
253
254 #[cfg(feature = "s3")]
255 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = "s3")))]
256 pub fn as_s3(&self) -> Option<&remi_s3::StorageService> {
259 match *self {
260 Self::S3(ref s3) => Some(s3),
261 _ => None,
262 }
263 }
264
265 #[cfg(feature = "azure")]
266 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = "azure")))]
267 pub fn as_azure(&self) -> Option<&remi_azure::StorageService> {
270 match *self {
271 Self::Azure(ref azure) => Some(azure),
272 _ => None,
273 }
274 }
275
276 #[cfg(feature = "gridfs")]
277 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = "gridfs")))]
278 pub fn as_gridfs(&self) -> Option<&remi_gridfs::StorageService> {
281 match *self {
282 Self::Gridfs(ref gridfs) => Some(gridfs),
283 _ => None,
284 }
285 }
286}
287
288#[remi::async_trait]
289#[allow(unused)]
290impl remi::StorageService for StorageService {
291 type Error = Error;
292
293 fn name(&self) -> Cow<'static, str> {
294 let name = match self {
295 #[cfg(feature = "fs")]
296 StorageService::Filesystem(service) => service.name(),
297
298 #[cfg(feature = "s3")]
299 StorageService::S3(service) => service.name(),
300
301 #[cfg(feature = "azure")]
302 StorageService::Azure(service) => service.name(),
303
304 #[cfg(feature = "gridfs")]
305 StorageService::Gridfs(service) => service.name(),
306
307 _ => Cow::Borrowed("<unknown>"),
308 };
309
310 Cow::Owned(format!("azalia:remi[{name}]"))
311 }
312
313 async fn init(&self) -> Result<(), Self::Error> {
314 match self {
315 #[cfg(feature = "fs")]
316 StorageService::Filesystem(service) => service.init().await.map_err(From::from),
317
318 #[cfg(feature = "s3")]
319 StorageService::S3(service) => service.init().await.map_err(From::from),
320
321 #[cfg(feature = "azure")]
322 StorageService::Azure(service) => service.init().await.map_err(From::from),
323
324 #[cfg(feature = "gridfs")]
325 StorageService::Gridfs(service) => service.init().await.map_err(From::from),
326
327 _ => unreachable!(),
328 }
329 }
330
331 async fn open<P: AsRef<Path> + Send>(&self, path: P) -> Result<Option<remi::Bytes>, Self::Error> {
332 match self {
333 #[cfg(feature = "fs")]
334 StorageService::Filesystem(service) => service.open(path).await.map_err(From::from),
335
336 #[cfg(feature = "s3")]
337 StorageService::S3(service) => service.open(path).await.map_err(From::from),
338
339 #[cfg(feature = "azure")]
340 StorageService::Azure(service) => service.open(path).await.map_err(From::from),
341
342 #[cfg(feature = "gridfs")]
343 StorageService::Gridfs(service) => service.open(path).await.map_err(From::from),
344
345 _ => unreachable!(),
346 }
347 }
348
349 async fn blob<P: AsRef<Path> + Send>(&self, path: P) -> Result<Option<remi::Blob>, Self::Error> {
350 match self {
351 #[cfg(feature = "fs")]
352 StorageService::Filesystem(service) => service.blob(path).await.map_err(From::from),
353
354 #[cfg(feature = "s3")]
355 StorageService::S3(service) => service.blob(path).await.map_err(From::from),
356
357 #[cfg(feature = "azure")]
358 StorageService::Azure(service) => service.blob(path).await.map_err(From::from),
359
360 #[cfg(feature = "gridfs")]
361 StorageService::Gridfs(service) => service.blob(path).await.map_err(From::from),
362
363 _ => unreachable!(),
364 }
365 }
366
367 async fn blobs<P: AsRef<Path> + Send>(
368 &self,
369 path: Option<P>,
370 options: Option<ListBlobsRequest>,
371 ) -> Result<Vec<remi::Blob>, Self::Error> {
372 match self {
373 #[cfg(feature = "fs")]
374 StorageService::Filesystem(service) => service.blobs(path, options).await.map_err(From::from),
375
376 #[cfg(feature = "s3")]
377 StorageService::S3(service) => service.blobs(path, options).await.map_err(From::from),
378
379 #[cfg(feature = "azure")]
380 StorageService::Azure(service) => service.blobs(path, options).await.map_err(From::from),
381
382 #[cfg(feature = "gridfs")]
383 StorageService::Gridfs(service) => service.blobs(path, options).await.map_err(From::from),
384
385 _ => unreachable!(),
386 }
387 }
388
389 async fn delete<P: AsRef<Path> + Send>(&self, path: P) -> Result<(), Self::Error> {
390 match self {
391 #[cfg(feature = "fs")]
392 StorageService::Filesystem(service) => service.delete(path).await.map_err(From::from),
393
394 #[cfg(feature = "s3")]
395 StorageService::S3(service) => service.delete(path).await.map_err(From::from),
396
397 #[cfg(feature = "azure")]
398 StorageService::Azure(service) => service.delete(path).await.map_err(From::from),
399
400 #[cfg(feature = "gridfs")]
401 StorageService::Gridfs(service) => service.delete(path).await.map_err(From::from),
402
403 _ => unreachable!(),
404 }
405 }
406
407 async fn exists<P: AsRef<Path> + Send>(&self, path: P) -> Result<bool, Self::Error> {
408 match self {
409 #[cfg(feature = "fs")]
410 StorageService::Filesystem(service) => service.exists(path).await.map_err(From::from),
411
412 #[cfg(feature = "s3")]
413 StorageService::S3(service) => service.exists(path).await.map_err(From::from),
414
415 #[cfg(feature = "azure")]
416 StorageService::Azure(service) => service.exists(path).await.map_err(From::from),
417
418 #[cfg(feature = "gridfs")]
419 StorageService::Gridfs(service) => service.exists(path).await.map_err(From::from),
420
421 _ => unreachable!(),
422 }
423 }
424
425 async fn upload<P: AsRef<Path> + Send>(&self, path: P, request: UploadRequest) -> Result<(), Self::Error> {
426 match self {
427 #[cfg(feature = "fs")]
428 StorageService::Filesystem(service) => service.upload(path, request).await.map_err(From::from),
429
430 #[cfg(feature = "s3")]
431 StorageService::S3(service) => service.upload(path, request).await.map_err(From::from),
432
433 #[cfg(feature = "azure")]
434 StorageService::Azure(service) => service.upload(path, request).await.map_err(From::from),
435
436 #[cfg(feature = "gridfs")]
437 StorageService::Gridfs(service) => service.upload(path, request).await.map_err(From::from),
438
439 _ => unreachable!(),
440 }
441 }
442
443 #[cfg(feature = "unstable")]
444 #[cfg_attr(any(noeldoc, docsrs), doc(cfg(feature = "unstable")))]
445 async fn healthcheck(&self) -> Result<(), Self::Error> {
446 match self {
447 #[cfg(feature = "fs")]
448 StorageService::Filesystem(service) => service.healthcheck().await.map_err(From::from),
449
450 #[cfg(feature = "s3")]
451 StorageService::S3(service) => service.healthcheck().await.map_err(From::from),
452
453 #[cfg(feature = "azure")]
454 StorageService::Azure(service) => service.healthcheck().await.map_err(From::from),
455
456 #[cfg(feature = "gridfs")]
457 StorageService::Gridfs(service) => service.healthcheck().await.map_err(From::from),
458
459 _ => unreachable!(),
460 }
461 }
462}