use crate::forms::{
field::RuniqueForm,
prisme::{aegis, csrf_gate, sentinel},
};
use crate::utils::{
aliases::{ARuniqueConfig, ATera, StrMap, StrVecMap},
trad::t,
};
use axum::{
body::Body,
extract::{FromRequest, FromRequestParts, Path},
http::{Request, StatusCode},
response::{IntoResponse, Response},
};
use std::collections::HashMap;
pub struct Prisme<T>(pub T);
pub async fn prisme<S, T>(req: Request<Body>, state: &S) -> Result<Prisme<T>, Response>
where
S: Send + Sync,
T: RuniqueForm,
{
let tera = req.extensions().get::<ATera>().cloned().ok_or_else(|| {
(
StatusCode::INTERNAL_SERVER_ERROR,
t("forms.tera_not_configured").to_string(),
)
.into_response()
})?;
let config = req
.extensions()
.get::<ARuniqueConfig>()
.cloned()
.ok_or_else(|| {
(
StatusCode::INTERNAL_SERVER_ERROR,
t("forms.config_not_found").to_string(),
)
.into_response()
})?;
sentinel(&req, &config).map_err(|boxed| *boxed)?;
let csrf_session = req
.extensions()
.get::<crate::utils::csrf::CsrfToken>()
.cloned()
.ok_or_else(|| {
(
StatusCode::INTERNAL_SERVER_ERROR,
t("csrf.missing").to_string(),
)
.into_response()
})?;
let method = req.method().clone();
let content_type = req
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("")
.to_string();
let (mut parts, body) = req.into_parts();
let path_params = Path::<HashMap<String, String>>::from_request_parts(&mut parts, state)
.await
.map(|Path(p)| p)
.unwrap_or_default();
let query_params = parts
.uri
.query()
.and_then(|q| serde_urlencoded::from_str::<HashMap<String, String>>(q).ok())
.unwrap_or_default();
let req = Request::from_parts(parts, body);
let parsed = aegis(req, state, config.clone(), &content_type).await?;
if let Some(prisme) =
csrf_gate::<T>(&parsed, csrf_session.as_str(), tera.clone(), &method).await?
{
return Ok(prisme);
}
let form_data = convert_for_form(parsed);
let csrf_for_form = csrf_session
.masked()
.unwrap_or_else(|_| csrf_session.clone());
let mut form = T::build_with_data(&form_data, tera, csrf_for_form.as_str(), method).await;
form.get_form_mut()
.set_url_params(path_params, query_params);
Ok(Prisme(form))
}
impl<S, T> FromRequest<S> for Prisme<T>
where
S: Send + Sync,
T: RuniqueForm,
{
type Rejection = Response;
async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
prisme(req, state).await
}
}
fn convert_for_form(parsed: StrVecMap) -> StrMap {
parsed.into_iter().map(|(k, v)| (k, v.join(","))).collect()
}