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}