kegani 0.1.0

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! Extractor traits and implementations for request data extraction
//!
//! Provides `Path<T>`, `Query<T>`, `State<T>` extractors.

use actix_web::{FromRequest, HttpRequest, dev::Payload, web};
use serde::de::DeserializeOwned;
use std::future::{ready, Ready};

/// Path parameter extractor
///
/// Extracts a path parameter and deserializes it to type `T`.
#[derive(Debug)]
pub struct Path<T>(pub T);

impl<T: DeserializeOwned + 'static> FromRequest for Path<T> {
    type Error = actix_web::Error;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        let mut map = serde_json::Map::new();
        for (key, value) in req.match_info().iter() {
            map.insert(key.to_string(), serde_json::json!(value));
        }

        match T::deserialize(serde_json::Value::Object(map)) {
            Ok(data) => ready(Ok(Path(data))),
            Err(e) => ready(Err(actix_web::error::ErrorBadRequest(e))),
        }
    }
}

/// Query parameter extractor
///
/// Extracts and deserializes query parameters to type `T`.
#[derive(Debug)]
pub struct Query<T>(pub T);

impl<T: DeserializeOwned + 'static> FromRequest for Query<T> {
    type Error = actix_web::Error;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        match serde_urlencoded::from_str::<T>(req.query_string()) {
            Ok(data) => ready(Ok(Query(data))),
            Err(e) => ready(Err(actix_web::error::ErrorBadRequest(e))),
        }
    }
}

/// JSON body extractor
///
/// Extracts and deserializes JSON body to type `T`.
#[derive(Debug)]
pub struct Json<T>(pub T);

impl<T: DeserializeOwned + 'static> FromRequest for Json<T> {
    type Error = actix_web::Error;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(_req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
        ready(Err(actix_web::error::ErrorNotImplemented(
            "Json extractor requires web::Json<T> from actix-web",
        )))
    }
}

/// Application state extractor
///
/// Extracts application state of type `T`.
#[derive(Debug, Clone)]
pub struct State<T>(pub T);

impl<T: Clone + Send + Sync + 'static> FromRequest for State<T> {
    type Error = actix_web::Error;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        ready(
            req.app_data::<web::Data<T>>()
                .map(|data| State(data.as_ref().clone()))
                .ok_or_else(|| actix_web::error::ErrorInternalServerError("State not found"))
        )
    }
}