lexa_framework/extract/
form.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ Copyright: (c) 2023, Mike 'PhiSyX' S. (https://github.com/PhiSyX)         ┃
3// ┃ SPDX-License-Identifier: MPL-2.0                                          ┃
4// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
5// ┃                                                                           ┃
6// ┃  This Source Code Form is subject to the terms of the Mozilla Public      ┃
7// ┃  License, v. 2.0. If a copy of the MPL was not distributed with this      ┃
8// ┃  file, You can obtain one at https://mozilla.org/MPL/2.0/.                ┃
9// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
10
11use hyper::http;
12
13// --------- //
14// Structure //
15// --------- //
16
17pub struct Form<F>(pub F);
18
19// ----------- //
20// Énumération //
21// ----------- //
22
23#[derive(Debug)]
24#[derive(thiserror::Error)]
25#[error("\n\t{}: {0}", std::any::type_name::<Self>())]
26pub enum MissingFormError {
27	FormRejection(#[from] axum::extract::rejection::FormRejection),
28	JsonRejection(#[from] axum::extract::rejection::JsonRejection),
29}
30
31// -------------- //
32// Implémentation // -> Interface
33// -------------- //
34
35#[axum::async_trait]
36impl<F, S, B> axum::extract::FromRequest<S, B> for Form<F>
37where
38	F: serde::de::DeserializeOwned,
39	S: Send + Sync,
40	B: hyper::body::HttpBody + Send + 'static,
41	B::Data: Send,
42	B::Error: Into<axum::BoxError>,
43{
44	type Rejection = MissingFormError;
45
46	async fn from_request(
47		req: axum::http::request::Request<B>,
48		state: &S,
49	) -> Result<Self, Self::Rejection> {
50		let f = if json_content_type(req.headers()) {
51			let axum::extract::Json(form) =
52				axum::extract::Json::<F>::from_request(req, state).await?;
53			form
54		} else {
55			let axum::extract::Form(form) =
56				axum::extract::Form::<F>::from_request(req, state).await?;
57			form
58		};
59		Ok(Self(f))
60	}
61}
62
63impl axum::response::IntoResponse for MissingFormError {
64	fn into_response(self) -> axum::response::Response {
65		let err_status = http::StatusCode::INTERNAL_SERVER_ERROR;
66		let err_body = self.to_string();
67		(err_status, err_body).into_response()
68	}
69}
70
71fn json_content_type(headers: &hyper::HeaderMap) -> bool {
72	let content_type =
73		if let Some(content_type) = headers.get(hyper::header::CONTENT_TYPE) {
74			content_type
75		} else {
76			return false;
77		};
78
79	let content_type = if let Ok(content_type) = content_type.to_str() {
80		content_type
81	} else {
82		return false;
83	};
84
85	let mime = if let Ok(mime) = content_type.parse::<mime::Mime>() {
86		mime
87	} else {
88		return false;
89	};
90
91	let is_json_content_type = mime.type_() == "application"
92		&& (mime.subtype() == "json"
93			|| mime.suffix().map_or(false, |name| name == "json"));
94
95	is_json_content_type
96}