use bytes::Bytes;
use http::HeaderValue;
use http_body_util::{BodyExt, Full};
use crate::{
BoxError,
body::{
Body,
codec::{BodyContentType, BodyDecoder, BodyEncoder},
},
};
#[derive(Clone, Debug)]
pub struct TextEncoder {
pub content_type: HeaderValue,
}
impl Default for TextEncoder {
fn default() -> Self {
Self {
content_type: HeaderValue::from_static("text/plain; charset=utf-8"),
}
}
}
impl TextEncoder {
pub fn with_content_type(content_type: HeaderValue) -> Self {
Self { content_type }
}
}
impl BodyContentType for TextEncoder {
fn content_type(&self) -> HeaderValue {
self.content_type.clone()
}
}
impl BodyEncoder<&str> for TextEncoder {
type Error = std::convert::Infallible;
fn encode(&self, data: &str) -> Result<Body, Self::Error> {
Ok(Body::new(Full::new(Bytes::copy_from_slice(
data.as_bytes(),
))))
}
}
impl BodyEncoder<&String> for TextEncoder {
type Error = std::convert::Infallible;
fn encode(&self, data: &String) -> Result<Body, Self::Error> {
<Self as BodyEncoder<&str>>::encode(self, data.as_str())
}
}
#[derive(Clone, Debug, Default)]
pub struct TextDecoder;
#[derive(Debug, thiserror::Error)]
pub enum TextDecodeError {
#[error("body read error: {0}")]
Body(#[source] BoxError),
#[error("utf-8 decode error: {0}")]
Utf8(#[from] std::string::FromUtf8Error),
}
impl BodyDecoder<String> for TextDecoder {
type Error = TextDecodeError;
async fn decode<B>(&self, body: B) -> Result<String, Self::Error>
where
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
B::Error: Into<BoxError>,
{
let bytes = body
.collect()
.await
.map_err(|e| TextDecodeError::Body(e.into()))?
.to_bytes();
String::from_utf8(bytes.to_vec()).map_err(TextDecodeError::Utf8)
}
}