use bytes::Bytes;
use http::HeaderValue;
use http_body_util::{BodyExt, Full};
use serde::{Serialize, de::DeserializeOwned};
use crate::{
BoxError,
body::{
Body,
codec::{BodyContentType, BodyDecoder, BodyEncoder},
},
};
pub type XmlEncodeError = quick_xml::SeError;
#[derive(Clone, Debug)]
pub struct XmlEncoder {
pub content_type: HeaderValue,
}
impl Default for XmlEncoder {
fn default() -> Self {
Self {
content_type: HeaderValue::from_static("application/xml"),
}
}
}
impl XmlEncoder {
pub fn with_content_type(content_type: HeaderValue) -> Self {
Self { content_type }
}
}
impl BodyContentType for XmlEncoder {
fn content_type(&self) -> HeaderValue {
self.content_type.clone()
}
}
impl<T: Serialize> BodyEncoder<&T> for XmlEncoder {
type Error = quick_xml::SeError;
fn encode(&self, data: &T) -> Result<Body, Self::Error> {
let serialised = quick_xml::se::to_string(data)?;
Ok(Body::new(Full::new(Bytes::from(serialised.into_bytes()))))
}
}
#[derive(Clone, Debug, Default)]
pub struct XmlDecoder;
#[derive(Debug, thiserror::Error)]
pub enum XmlDecodeError {
#[error("body read error: {0}")]
Body(#[source] BoxError),
#[error("xml decode error: {0}")]
Xml(#[from] quick_xml::DeError),
}
impl<O> BodyDecoder<O> for XmlDecoder
where
O: DeserializeOwned,
{
type Error = XmlDecodeError;
async fn decode<B>(&self, body: B) -> Result<O, Self::Error>
where
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
B::Error: Into<BoxError>,
{
let bytes = body
.collect()
.await
.map_err(|e| XmlDecodeError::Body(e.into()))?
.to_bytes();
let text = std::str::from_utf8(bytes.as_ref()).map_err(|e| {
XmlDecodeError::Xml(quick_xml::DeError::Custom(format!(
"non-UTF-8 XML payload: {e}"
)))
})?;
Ok(quick_xml::de::from_str(text)?)
}
}