//! Extends `hyper::Body` with [`SputnikBody`]. use std::{future::Future, pin::Pin}; use hyper::http::{self, response::Builder}; use serde::de::DeserializeOwned; use crate::response::EmptyBuilder; impl EmptyBuilder for Builder { fn empty(self) -> http::Result> { self.body(hyper::Body::empty()) } } /// Adds deserialization methods to `hyper::Body`. pub trait SputnikBody { /// Parses a `application/x-www-form-urlencoded` request body into a given struct. fn into_form( self, ) -> Pin> + Send + Sync>>; /// Attempts to deserialize the request body as JSON. #[cfg(feature = "hyper_body_json")] #[cfg_attr(docsrs, doc(cfg(feature = "hyper_body_json")))] fn into_json( self, ) -> Pin> + Send + Sync>>; } impl SputnikBody for hyper::Body { fn into_form( self, ) -> Pin> + Send + Sync>> { Box::pin(async move { let full_body = hyper::body::to_bytes(self).await.map_err(BodyError)?; Ok(serde_urlencoded::from_bytes::(&full_body)?) }) } #[cfg(feature = "hyper_body_json")] #[cfg_attr(docsrs, doc(cfg(feature = "hyper_body_json")))] fn into_json( self, ) -> Pin> + Send + Sync>> { Box::pin(async move { let full_body = hyper::body::to_bytes(self).await.map_err(BodyError)?; Ok(serde_json::from_slice::(&full_body)?) }) } } #[derive(thiserror::Error, Debug)] #[error("failed to read body")] pub struct BodyError(pub hyper::Error); #[derive(thiserror::Error, Debug)] pub enum FormError { #[error("{0}")] Body(#[from] BodyError), #[error("form deserialize error: {0}")] Deserialize(#[from] serde_urlencoded::de::Error), } #[cfg(feature = "hyper_body_json")] #[cfg_attr(docsrs, doc(cfg(feature = "hyper_body_json")))] #[derive(thiserror::Error, Debug)] pub enum JsonError { #[error("{0}")] Body(#[from] BodyError), #[error("json deserialize error: {0}")] Deserialize(#[from] serde_json::Error), }