Skip to main content

axum_toml/
lib.rs

1use axum::{
2    async_trait,
3    body::HttpBody,
4    extract::FromRequest,
5    http::{header, HeaderValue, Request, StatusCode},
6    response::{IntoResponse, Response},
7    BoxError,
8};
9
10const TOML_MIME: &str = "application/toml";
11const TEXT_UTF8_MIME: &str = "text/plain; charset=utf-8";
12
13#[derive(Debug, thiserror::Error)]
14pub enum TomlRejection {
15    #[error("Failed to deserialize the request body")]
16    DeserializationError(#[from] toml::de::Error),
17    #[error("Request body didn't contain valid UTF-8")]
18    StringRejection(#[from] axum::extract::rejection::StringRejection),
19}
20
21impl IntoResponse for TomlRejection {
22    fn into_response(self) -> Response {
23        match self {
24            Self::DeserializationError(err) => (
25                StatusCode::BAD_REQUEST,
26                [(
27                    header::CONTENT_TYPE,
28                    HeaderValue::from_static(TEXT_UTF8_MIME),
29                )],
30                err.to_string(),
31            )
32                .into_response(),
33            Self::StringRejection(err) => err.into_response(),
34        }
35    }
36}
37
38pub struct Toml<T>(pub T);
39
40#[async_trait]
41impl<T, S, B> FromRequest<S, B> for Toml<T>
42where
43    T: serde::de::DeserializeOwned,
44    B: HttpBody + Send + 'static,
45    B::Data: Send,
46    B::Error: Into<BoxError>,
47    S: Send + Sync,
48{
49    type Rejection = TomlRejection;
50
51    async fn from_request(req: Request<B>, state: &S) -> Result<Self, Self::Rejection> {
52        let text = String::from_request(req, state).await?;
53        Ok(Toml(toml::from_str(&text)?))
54    }
55}
56
57impl<T> IntoResponse for Toml<T>
58where
59    T: serde::Serialize,
60{
61    fn into_response(self) -> Response {
62        match toml::to_string(&self.0) {
63            Ok(serialized) => (
64                StatusCode::OK,
65                [(header::CONTENT_TYPE, HeaderValue::from_static(TOML_MIME))],
66                serialized,
67            )
68                .into_response(),
69            Err(err) => (
70                StatusCode::INTERNAL_SERVER_ERROR,
71                [(
72                    header::CONTENT_TYPE,
73                    HeaderValue::from_static(TEXT_UTF8_MIME),
74                )],
75                err.to_string(),
76            )
77                .into_response(),
78        }
79    }
80}