modo_upload/lib.rs
1//! File upload support for modo applications.
2//!
3//! Provides multipart form parsing, in-memory buffering, pluggable storage
4//! backends, and file validation. The `#[derive(FromMultipart)]`
5//! macro generates the boilerplate for mapping multipart fields to struct fields.
6//!
7//! # Features
8//!
9//! - `local` (default) — local filesystem storage via [`storage::local::LocalStorage`].
10//! - `opendal` — S3-compatible object storage via `storage::opendal::OpendalStorage`
11//! and the `S3Config` configuration type.
12//!
13//! # Quick start
14//!
15//! ```rust,ignore
16//! use modo_upload::{FromMultipart, MultipartForm, UploadedFile, storage};
17//! use modo_upload::UploadConfig;
18//!
19//! #[derive(FromMultipart)]
20//! struct UploadForm {
21//! #[upload(max_size = "5mb", accept = "image/*")]
22//! avatar: UploadedFile,
23//! name: String,
24//! }
25//!
26//! #[modo::handler(POST, "/upload")]
27//! async fn upload(form: MultipartForm<UploadForm>) -> modo::JsonResult<()> {
28//! let storage = storage(&UploadConfig::default())?;
29//! let stored = storage.store("avatars", &form.avatar).await?;
30//! println!("stored at {}", stored.path);
31//! Ok(modo::Json(()))
32//! }
33//! ```
34
35pub use modo_upload_macros::FromMultipart;
36
37mod config;
38mod extractor;
39mod file;
40pub mod storage;
41mod stream;
42mod validate;
43
44#[cfg(feature = "opendal")]
45pub use config::S3Config;
46pub use config::{StorageBackend, UploadConfig};
47pub use extractor::MultipartForm;
48pub use file::UploadedFile;
49pub use storage::{FileStorage, StoredFile, storage};
50pub use stream::BufferedUpload;
51pub use validate::{gb, kb, mb};
52
53/// Trait for parsing a struct from `multipart/form-data`.
54///
55/// Implement this trait (or derive it with `#[derive(FromMultipart)]`) to
56/// describe how multipart fields map to struct fields. The
57/// [`MultipartForm`] extractor calls this automatically during request
58/// extraction.
59#[async_trait::async_trait]
60pub trait FromMultipart: Sized {
61 /// Parse `multipart` into `Self`, enforcing `max_file_size` on every file
62 /// field when `Some`.
63 async fn from_multipart(
64 multipart: &mut axum::extract::Multipart,
65 max_file_size: Option<usize>,
66 ) -> Result<Self, modo::Error>;
67}
68
69/// Internal helpers exposed for use by generated code. Not public API.
70#[doc(hidden)]
71pub mod __internal {
72 pub use crate::validate::mime_matches;
73 pub use async_trait::async_trait;
74 pub use axum;
75}