Skip to main content

modo_upload/storage/
types.rs

1use crate::file::UploadedFile;
2use crate::stream::BufferedUpload;
3use std::future::Future;
4use std::pin::Pin;
5
6/// Metadata returned after a file has been successfully stored.
7pub struct StoredFile {
8    /// Relative path within the storage backend (e.g. `"avatars/01HXK3Q1A2B3.jpg"`).
9    pub path: String,
10    /// File size in bytes.
11    pub size: u64,
12}
13
14/// Trait for persisting uploaded files to a storage backend.
15///
16/// Both in-memory ([`UploadedFile`]) and chunked ([`BufferedUpload`]) uploads
17/// are supported.  Implementors must be `Sync + 'static` so they can be
18/// shared across async tasks behind an `Arc<dyn FileStorageDyn>`.
19///
20/// Use the [`storage()`](crate::storage()) factory function to construct the
21/// backend configured by [`UploadConfig`](crate::UploadConfig), or instantiate
22/// a concrete backend directly (e.g. [`LocalStorage`](super::local::LocalStorage)).
23///
24/// Use [`FileStorageDyn`] (object-safe companion) for trait objects:
25/// `Arc<dyn FileStorageDyn>`. Any type implementing `FileStorage`
26/// automatically implements `FileStorageDyn` via a blanket impl.
27#[trait_variant::make(FileStorageSend: Send)]
28pub trait FileStorage: Sync + 'static {
29    /// Store a buffered in-memory file under `prefix/`.
30    ///
31    /// A ULID-based unique filename is generated automatically.
32    /// Returns the stored path and size on success.
33    async fn store(&self, prefix: &str, file: &UploadedFile) -> Result<StoredFile, modo::Error>;
34
35    /// Store a chunked upload under `prefix/`.
36    ///
37    /// Chunks are consumed from `stream` sequentially.
38    /// Returns the stored path and size on success.
39    async fn store_stream(
40        &self,
41        prefix: &str,
42        stream: &mut BufferedUpload,
43    ) -> Result<StoredFile, modo::Error>;
44
45    /// Delete a file by its storage path (as returned by [`store`](Self::store)).
46    async fn delete(&self, path: &str) -> Result<(), modo::Error>;
47
48    /// Return `true` if a file exists at the given storage path.
49    async fn exists(&self, path: &str) -> Result<bool, modo::Error>;
50}
51
52/// Object-safe companion to [`FileStorage`] for use with `Arc<dyn FileStorageDyn>`.
53///
54/// This trait is automatically implemented for all types that implement
55/// [`FileStorage`] (or `FileStorageSend`).
56pub trait FileStorageDyn: Send + Sync + 'static {
57    /// Store a buffered in-memory file under `prefix/`.
58    ///
59    /// A ULID-based unique filename is generated automatically.
60    /// Returns the stored path and size on success.
61    fn store<'a>(
62        &'a self,
63        prefix: &'a str,
64        file: &'a UploadedFile,
65    ) -> Pin<Box<dyn Future<Output = Result<StoredFile, modo::Error>> + Send + 'a>>;
66
67    /// Store a chunked upload under `prefix/`.
68    ///
69    /// Chunks are consumed from `stream` sequentially.
70    /// Returns the stored path and size on success.
71    fn store_stream<'a>(
72        &'a self,
73        prefix: &'a str,
74        stream: &'a mut BufferedUpload,
75    ) -> Pin<Box<dyn Future<Output = Result<StoredFile, modo::Error>> + Send + 'a>>;
76
77    /// Delete a file by its storage path (as returned by [`store`](Self::store)).
78    fn delete<'a>(
79        &'a self,
80        path: &'a str,
81    ) -> Pin<Box<dyn Future<Output = Result<(), modo::Error>> + Send + 'a>>;
82
83    /// Return `true` if a file exists at the given storage path.
84    fn exists<'a>(
85        &'a self,
86        path: &'a str,
87    ) -> Pin<Box<dyn Future<Output = Result<bool, modo::Error>> + Send + 'a>>;
88}
89
90impl<T: FileStorageSend> FileStorageDyn for T {
91    fn store<'a>(
92        &'a self,
93        prefix: &'a str,
94        file: &'a UploadedFile,
95    ) -> Pin<Box<dyn Future<Output = Result<StoredFile, modo::Error>> + Send + 'a>> {
96        Box::pin(FileStorageSend::store(self, prefix, file))
97    }
98
99    fn store_stream<'a>(
100        &'a self,
101        prefix: &'a str,
102        stream: &'a mut BufferedUpload,
103    ) -> Pin<Box<dyn Future<Output = Result<StoredFile, modo::Error>> + Send + 'a>> {
104        Box::pin(FileStorageSend::store_stream(self, prefix, stream))
105    }
106
107    fn delete<'a>(
108        &'a self,
109        path: &'a str,
110    ) -> Pin<Box<dyn Future<Output = Result<(), modo::Error>> + Send + 'a>> {
111        Box::pin(FileStorageSend::delete(self, path))
112    }
113
114    fn exists<'a>(
115        &'a self,
116        path: &'a str,
117    ) -> Pin<Box<dyn Future<Output = Result<bool, modo::Error>> + Send + 'a>> {
118        Box::pin(FileStorageSend::exists(self, path))
119    }
120}