kegani 0.1.1

A developer-friendly, ergonomic, production-ready Rust web framework
Documentation
//! Extractor traits and implementations for request data extraction
//!
//! Provides `Path<T>`, `Query<T>`, and `State<T>` extractors.
//!
//! For JSON body extraction, use `actix_web::web::Json<T>` directly in handlers.
//! Example:
//! ```ignore
//! async fn create_user(Json(user): Json<CreateUser>) -> Result<Json<User>, AppError> {
//!     // ...
//! }
//! ```

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

/// Path parameter extractor
///
/// Extracts a path parameter and deserializes it to type `T`.
///
/// # Example
/// ```ignore
/// #[get("/users/{id}")]
/// async fn get_user(Path(id): Path<uuid::Uuid>) -> Result<Json<User>, AppError> {
///     let id: uuid::Uuid = id.into();
///     // ...
/// }
/// ```
#[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`.
///
/// # Example
/// ```ignore
/// #[get("/users")]
/// async fn list_users(Query(pagination): Query<Pagination>) -> Result<Json<Vec<User>>, AppError> {
///     // pagination.page, pagination.size
/// }
/// ```
#[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))),
        }
    }
}

/// Application state extractor
///
/// Extracts application state of type `T` from `web::Data<T>`.
///
/// # Example
/// ```ignore
/// async fn get_user(State(state): State<MyState>) -> Result<Json<User>, AppError> {
///     // ...
/// }
/// ```
#[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::<actix_web::web::Data<T>>()
                .map(|data| State(data.as_ref().clone()))
                .ok_or_else(|| actix_web::error::ErrorInternalServerError("State not found"))
        )
    }
}