1use crate::FromMultipart;
2use axum::extract::FromRequest;
3use axum::http::Request;
4use modo::app::AppState;
5use modo::error::{Error, HttpError};
6use modo::validate::Validate;
7use std::ops::Deref;
8
9pub struct MultipartForm<T>(pub T);
21
22impl<T> Deref for MultipartForm<T> {
23 type Target = T;
24 fn deref(&self) -> &Self::Target {
25 &self.0
26 }
27}
28
29impl<T> MultipartForm<T> {
30 pub fn into_inner(self) -> T {
32 self.0
33 }
34}
35
36impl<T: Validate> MultipartForm<T> {
37 pub fn validate(&self) -> Result<(), Error> {
42 self.0.validate()
43 }
44}
45
46impl<T> FromRequest<AppState> for MultipartForm<T>
47where
48 T: FromMultipart + 'static,
49{
50 type Rejection = Error;
51
52 async fn from_request(
53 req: Request<axum::body::Body>,
54 state: &AppState,
55 ) -> Result<Self, Self::Rejection> {
56 let mut multipart = axum::extract::Multipart::from_request(req, state)
57 .await
58 .map_err(|e| HttpError::BadRequest.with_message(format!("{e}")))?;
59 let default_config = crate::config::UploadConfig::default();
60 let registered_config = state.services.get::<crate::config::UploadConfig>();
61 let config = registered_config.as_deref().unwrap_or(&default_config);
62 let max_file_size = config.max_file_size.as_ref().and_then(|s| {
63 modo::config::parse_size(s)
64 .inspect_err(|e| {
65 modo::tracing::warn!(
66 size = %s,
67 error = %e,
68 "failed to parse max_file_size from UploadConfig, ignoring limit"
69 );
70 })
71 .ok()
72 });
73 let mut value = T::from_multipart(&mut multipart, max_file_size).await?;
74 modo::sanitize::auto_sanitize(&mut value);
75 Ok(MultipartForm(value))
76 }
77}