Skip to main content

ferro_rs/http/
extract.rs

1//! Request extraction traits for handler parameter injection
2//!
3//! This module provides the `FromRequest` trait which enables the `#[handler]`
4//! macro to automatically extract typed parameters from incoming requests.
5
6use super::Request;
7use crate::error::FrameworkError;
8use async_trait::async_trait;
9
10/// Trait for types that can be extracted from an HTTP request
11///
12/// This trait is used by the `#[handler]` macro to automatically
13/// extract and inject typed parameters into controller handlers.
14///
15/// # Implementations
16///
17/// - `Request` - passes the request through unchanged
18/// - Any type implementing `FormRequest` - automatically parses and validates
19///
20/// # Example
21///
22/// The `#[handler]` macro uses this trait to transform:
23///
24/// ```rust,ignore
25/// #[handler]
26/// pub async fn store(form: CreateUserRequest) -> Response {
27///     // ...
28/// }
29/// ```
30///
31/// Into:
32///
33/// ```rust,ignore
34/// pub async fn store(req: Request) -> Response {
35///     let form = <CreateUserRequest as FromRequest>::from_request(req).await?;
36///     // ...
37/// }
38/// ```
39#[async_trait]
40pub trait FromRequest: Sized + Send {
41    /// Extract Self from the incoming request
42    ///
43    /// Returns `Err(FrameworkError)` if extraction fails, which will be
44    /// converted to an appropriate HTTP error response.
45    async fn from_request(req: Request) -> Result<Self, FrameworkError>;
46}
47
48/// Request passes through unchanged
49#[async_trait]
50impl FromRequest for Request {
51    async fn from_request(req: Request) -> Result<Self, FrameworkError> {
52        Ok(req)
53    }
54}
55
56/// Trait for types that can be extracted from a single path parameter
57///
58/// This trait enables automatic extraction of typed values from route parameters
59/// like `/users/{id}` where `{id}` can be extracted as an `i32`.
60///
61/// # Example
62///
63/// The `#[handler]` macro uses this trait to transform:
64///
65/// ```rust,ignore
66/// #[handler]
67/// pub async fn show(id: i32, slug: String) -> Response {
68///     // ...
69/// }
70/// ```
71///
72/// Into code that extracts `id` and `slug` from the route parameters.
73pub trait FromParam: Sized {
74    /// Extract Self from a string parameter value
75    ///
76    /// Returns `Err(FrameworkError)` if extraction fails, which will be
77    /// converted to an appropriate HTTP error response (400 Bad Request).
78    fn from_param(value: &str) -> Result<Self, FrameworkError>;
79}
80
81impl FromParam for String {
82    fn from_param(value: &str) -> Result<Self, FrameworkError> {
83        Ok(value.to_string())
84    }
85}
86
87impl FromParam for i32 {
88    fn from_param(value: &str) -> Result<Self, FrameworkError> {
89        value
90            .parse()
91            .map_err(|_| FrameworkError::param_parse(value, "i32"))
92    }
93}
94
95impl FromParam for i64 {
96    fn from_param(value: &str) -> Result<Self, FrameworkError> {
97        value
98            .parse()
99            .map_err(|_| FrameworkError::param_parse(value, "i64"))
100    }
101}
102
103impl FromParam for u32 {
104    fn from_param(value: &str) -> Result<Self, FrameworkError> {
105        value
106            .parse()
107            .map_err(|_| FrameworkError::param_parse(value, "u32"))
108    }
109}
110
111impl FromParam for u64 {
112    fn from_param(value: &str) -> Result<Self, FrameworkError> {
113        value
114            .parse()
115            .map_err(|_| FrameworkError::param_parse(value, "u64"))
116    }
117}
118
119impl FromParam for usize {
120    fn from_param(value: &str) -> Result<Self, FrameworkError> {
121        value
122            .parse()
123            .map_err(|_| FrameworkError::param_parse(value, "usize"))
124    }
125}