nestforge 1.9.0

NestJS-inspired modular backend framework for Rust
Documentation
use axum::{
    body::Body,
    http::{Request, StatusCode},
};
use tower::ServiceExt;

struct CorrelationId;

impl nestforge::RequestDecorator for CorrelationId {
    type Output = String;

    fn extract(
        _ctx: &nestforge::RequestContext,
        parts: &axum::http::request::Parts,
    ) -> Result<Self::Output, nestforge::HttpException> {
        parts
            .headers
            .get("x-correlation-id")
            .and_then(|value| value.to_str().ok())
            .map(str::to_string)
            .ok_or_else(|| nestforge::HttpException::bad_request("Missing x-correlation-id"))
    }
}

#[nestforge::controller("/decorators")]
#[derive(Default)]
struct DecoratorController;

#[nestforge::routes]
impl DecoratorController {
    #[nestforge::get("/correlation")]
    async fn correlation_id(
        id: nestforge::Decorated<CorrelationId>,
    ) -> nestforge::ApiResult<String> {
        Ok(axum::Json(id.into_inner()))
    }
}

#[nestforge::module(controllers = [DecoratorController])]
#[derive(Default)]
struct DecoratorModule;

impl DecoratorModule {}

#[tokio::test]
async fn custom_request_decorators_can_extract_values_from_request_parts() {
    let app = nestforge::NestForgeFactory::<DecoratorModule>::create()
        .expect("factory")
        .into_router();

    let response = app
        .oneshot(
            Request::builder()
                .uri("/decorators/correlation")
                .header("x-correlation-id", "corr-42")
                .body(Body::empty())
                .expect("request"),
        )
        .await
        .expect("response");

    assert_eq!(response.status(), StatusCode::OK);
}