webrune 0.1.2

A composable web server.
Documentation
use crate::{FromRequest, Request};
use crate::{IntoResponse, Response};
use http::header::CONTENT_TYPE;
use http::{HeaderValue, StatusCode};
use http_body_util::BodyExt;
use http_body_util::Full;
use hyper::body::Bytes;
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::fmt::{Display, Formatter};
use std::ops::Deref;

/// JSON request and response body wrapper.
///
/// `Json<T>` serves two purposes:
///
/// - **Response construction**: serialize `T` into a JSON response
/// - **Request extraction**: deserialize a JSON request body into `T`
///
/// This type integrates with:
/// - [`IntoResponse`] for returning JSON responses, and
/// - [`FromRequest`] for extracting JSON request bodies.
///
/// # Examples
///
/// ## Returning JSON
///
/// ```ignore
/// async fn handler(request: Request, state: MyState) -> Result<Json<User>, ()> {
///     ...
///     Ok(Json(user))
/// }
/// ```
///
/// ## Extracting JSON
///
/// ```ignore
/// async fn handler(request: Request, state: MyState) -> Result<String, ()> {
///     let body = request.body::<Json<UserData>>().await?;
///     ...
/// }
/// ```
///
/// In both cases, serialization and deserialization are handled automatically
/// using [`serde_json`].
pub struct Json<T>(pub T);

impl<T> Deref for Json<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

/// Converts a value into a JSON HTTP response.
///
/// The value is serialized using [`serde_json`] and returned with a
/// `Content-Type: application/json` header.
///
/// # Panics
///
/// Panics if serialization fails. This should only occur if `T`’s
/// [`Serialize`] implementation is incorrect.
impl<T> IntoResponse for Json<T>
where
    T: Serialize,
{
    fn into_response(self) -> Response {
        let bytes = serde_json::to_vec(&self.0).expect("Serializable value could not serialize");

        let mut response = Response::new(Full::new(Bytes::from(bytes)));

        response
            .headers_mut()
            .append(CONTENT_TYPE, HeaderValue::from_static("application/json"));

        response
    }
}

/// Extracts and deserializes a JSON request body.
///
/// The request body is fully buffered and deserialized using [`serde_json`].
///
/// # Errors
///
/// Extraction fails if:
/// - the request body cannot be read, or
/// - the body does not contain valid JSON for type `T`.
impl<T> FromRequest for Json<T>
where
    T: DeserializeOwned,
{
    type Error = JsonError;
    type Output = T;

    async fn from_request(request: Request) -> Result<Self::Output, Self::Error> {
        let bytes = request
            .into_inner()
            .collect()
            .await
            .map_err(|e| JsonError(e.to_string()))?
            .to_bytes();

        let serialized =
            serde_json::from_slice(bytes.as_ref()).map_err(|e| JsonError(e.to_string()))?;

        Ok(serialized)
    }
}

/// Error returned when JSON extraction fails.
pub struct JsonError(pub String);

impl Display for JsonError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Converts a [`JsonError`] into a `400 Bad Request` response.
///
/// The response body is empty by default.
impl IntoResponse for JsonError {
    fn into_response(self) -> Response {
        let mut response = Response::new(Full::new(Bytes::new()));
        *response.status_mut() = StatusCode::BAD_REQUEST;
        response
    }
}