rustapi_core/
request.rs

1//! Request types for RustAPI
2//!
3//! This module provides the [`Request`] type which wraps an incoming HTTP request
4//! and provides access to all its components.
5//!
6//! # Accessing Request Data
7//!
8//! While extractors are the preferred way to access request data in handlers,
9//! the `Request` type provides direct access when needed:
10//!
11//! ```rust,ignore
12//! // In middleware or custom extractors
13//! fn process_request(req: &Request) {
14//!     let method = req.method();
15//!     let path = req.path();
16//!     let headers = req.headers();
17//!     let query = req.query_string();
18//! }
19//! ```
20//!
21//! # Path Parameters
22//!
23//! Path parameters extracted from the URL pattern are available via:
24//!
25//! ```rust,ignore
26//! // For route "/users/{id}"
27//! let id = req.path_param("id");
28//! let all_params = req.path_params();
29//! ```
30//!
31//! # Request Body
32//!
33//! The body can only be consumed once:
34//!
35//! ```rust,ignore
36//! if let Some(body) = req.take_body() {
37//!     // Process body bytes
38//! }
39//! // Subsequent calls return None
40//! ```
41
42use bytes::Bytes;
43use http::{request::Parts, Extensions, HeaderMap, Method, Uri, Version};
44use std::collections::HashMap;
45use std::sync::Arc;
46
47/// HTTP Request wrapper
48///
49/// Provides access to all parts of an incoming HTTP request.
50pub struct Request {
51    pub(crate) parts: Parts,
52    pub(crate) body: Option<Bytes>,
53    pub(crate) state: Arc<Extensions>,
54    pub(crate) path_params: HashMap<String, String>,
55}
56
57impl Request {
58    /// Create a new request from parts
59    pub(crate) fn new(
60        parts: Parts,
61        body: Bytes,
62        state: Arc<Extensions>,
63        path_params: HashMap<String, String>,
64    ) -> Self {
65        Self {
66            parts,
67            body: Some(body),
68            state,
69            path_params,
70        }
71    }
72
73    /// Get the HTTP method
74    pub fn method(&self) -> &Method {
75        &self.parts.method
76    }
77
78    /// Get the URI
79    pub fn uri(&self) -> &Uri {
80        &self.parts.uri
81    }
82
83    /// Get the HTTP version
84    pub fn version(&self) -> Version {
85        self.parts.version
86    }
87
88    /// Get the headers
89    pub fn headers(&self) -> &HeaderMap {
90        &self.parts.headers
91    }
92
93    /// Get request extensions
94    pub fn extensions(&self) -> &Extensions {
95        &self.parts.extensions
96    }
97
98    /// Get mutable extensions
99    pub fn extensions_mut(&mut self) -> &mut Extensions {
100        &mut self.parts.extensions
101    }
102
103    /// Get the request path
104    pub fn path(&self) -> &str {
105        self.parts.uri.path()
106    }
107
108    /// Get the query string
109    pub fn query_string(&self) -> Option<&str> {
110        self.parts.uri.query()
111    }
112
113    /// Take the body bytes (can only be called once)
114    pub fn take_body(&mut self) -> Option<Bytes> {
115        self.body.take()
116    }
117
118    /// Get path parameters
119    pub fn path_params(&self) -> &HashMap<String, String> {
120        &self.path_params
121    }
122
123    /// Get a specific path parameter
124    pub fn path_param(&self, name: &str) -> Option<&String> {
125        self.path_params.get(name)
126    }
127
128    /// Get shared state
129    pub fn state(&self) -> &Arc<Extensions> {
130        &self.state
131    }
132
133    /// Create a test request from an http::Request
134    ///
135    /// This is useful for testing middleware and extractors.
136    #[cfg(any(test, feature = "test-utils"))]
137    pub fn from_http_request<B>(req: http::Request<B>, body: Bytes) -> Self {
138        let (parts, _) = req.into_parts();
139        Self {
140            parts,
141            body: Some(body),
142            state: Arc::new(Extensions::new()),
143            path_params: HashMap::new(),
144        }
145    }
146}
147
148impl std::fmt::Debug for Request {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        f.debug_struct("Request")
151            .field("method", &self.parts.method)
152            .field("uri", &self.parts.uri)
153            .field("version", &self.parts.version)
154            .finish()
155    }
156}