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}