use crate::rejection::XmlRejection;
use axum_core::body::Body;
use axum_core::extract::{FromRequest, Request};
use axum_core::response::{IntoResponse, Response};
use bytes::Bytes;
use core::pin::Pin;
use http::{header, HeaderValue, StatusCode};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::future::Future;
use std::ops::{Deref, DerefMut};
mod rejection;
#[cfg(test)]
mod tests;
#[derive(Debug, Clone, Copy, Default)]
pub struct Xml<T>(pub T);
impl<T, S> FromRequest<S> for Xml<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = XmlRejection;
fn from_request<'state, 'future>(
req: Request<Body>,
state: &'state S,
) -> Pin<Box<dyn Future<Output = Result<Self, Self::Rejection>> + Send + 'future>>
where
'state: 'future,
Self: 'future,
{
Box::pin(async move {
let content_type = content_type(&req);
if !content_type.is_some_and(is_xml_type) {
return Err(XmlRejection::MissingXMLContentType);
}
let bytes = Bytes::from_request(req, state).await?;
println!("{:?}", bytes);
let value = quick_xml::de::from_reader(&*bytes)?;
Ok(Self(value))
})
}
}
fn content_type(req: &Request) -> Option<mime::Mime> {
req.headers()
.get(header::CONTENT_TYPE)
.and_then(|value| value.to_str().ok())
.and_then(|value| value.parse::<mime::Mime>().ok())
}
fn is_xml_type(mime: mime::Mime) -> bool {
let type_ = mime.type_();
(type_ == "application" || type_ == "text")
&& (mime.subtype() == "xml" || mime.suffix().is_some_and(|value| value == "xml"))
}
impl<T> Deref for Xml<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Xml<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<T> for Xml<T> {
fn from(inner: T) -> Self {
Self(inner)
}
}
impl<T> IntoResponse for Xml<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match quick_xml::se::to_string(&self.0) {
Ok(value) => (
[(
header::CONTENT_TYPE,
HeaderValue::from_static("application/xml"),
)],
value,
)
.into_response(),
Err(err) => (
StatusCode::INTERNAL_SERVER_ERROR,
[(
header::CONTENT_TYPE,
HeaderValue::from_static(mime::TEXT_PLAIN_UTF_8.as_ref()),
)],
err.to_string(),
)
.into_response(),
}
}
}