lambda_lw_http_router_core/route_context.rs
1use lambda_runtime::tracing::Span;
2use opentelemetry::{Key as OtelKey, Value as OtelValue};
3use std::collections::HashMap;
4use std::sync::Arc;
5use tracing_opentelemetry::OpenTelemetrySpanExt;
6
7/// Context passed to route handlers containing request information and application state.
8///
9/// This struct provides access to request details, path parameters, application state,
10/// and Lambda execution context. It is passed to every route handler and provides
11/// methods for accessing OpenTelemetry span attributes.
12///
13/// # Examples
14///
15/// ```rust
16/// use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
17/// use lambda_lw_http_router_core::{RouteContext, Router};
18/// use serde_json::Value;
19/// use lambda_runtime::Error;
20/// use serde_json::json;
21///
22/// async fn handle_user(ctx: RouteContext<(), ApiGatewayV2httpRequest>) -> Result<Value, Error> {
23/// // Get path parameters
24/// let user_id = ctx.get_param("id").unwrap_or_else(||"default".to_string());
25///
26/// // Access request details
27/// let method = ctx.method();
28/// let path = ctx.path();
29///
30/// // Set span attributes
31/// ctx.set_otel_attribute("user.id", user_id.clone());
32///
33/// Ok(json!({ "id": user_id }))
34/// }
35/// ```
36#[derive(Debug, Clone)]
37pub struct RouteContext<State: Clone, E> {
38 /// The full request path
39 pub path: String,
40 /// The HTTP method (GET, POST, etc.)
41 pub method: String,
42 /// Path parameters extracted from the URL (e.g., {id} -> "123")
43 pub params: HashMap<String, String>,
44 /// Application state shared across all requests
45 pub state: Arc<State>,
46 /// The original Lambda event
47 pub event: E,
48 /// Lambda execution context
49 pub lambda_context: lambda_runtime::Context,
50 /// The route template pattern (e.g., "/quote/{id}")
51 pub route_pattern: String,
52}
53
54impl<State: Clone, E> RouteContext<State, E> {
55 /// Returns the full request path of the current request.
56 ///
57 /// # Examples
58 ///
59 /// ```rust
60 /// # use lambda_lw_http_router_core::{RouteContext, Router};
61 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
62 /// # #[derive(Clone)]
63 /// # struct AppState {}
64 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
65 /// let path = ctx.path();
66 /// assert_eq!(path, "/users/123/profile");
67 /// }
68 /// ```
69 pub fn path(&self) -> &str {
70 &self.path
71 }
72
73 /// Returns the HTTP method of the current request (e.g., "GET", "POST").
74 ///
75 /// # Examples
76 ///
77 /// ```rust
78 /// # use lambda_lw_http_router_core::{RouteContext, Router};
79 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
80 /// # #[derive(Clone)]
81 /// # struct AppState {}
82 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
83 /// let method = ctx.method();
84 /// assert_eq!(method, "POST");
85 /// }
86 /// ```
87 pub fn method(&self) -> &str {
88 &self.method
89 }
90
91 /// Returns a reference to the shared application state.
92 ///
93 /// The state is shared across all request handlers and can be used to store
94 /// application-wide data like database connections or configuration.
95 ///
96 /// # Examples
97 ///
98 /// ```rust
99 /// # use lambda_lw_http_router_core::{RouteContext, Router};
100 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
101 /// #[derive(Clone)]
102 /// struct AppState {
103 /// api_key: String,
104 /// }
105 ///
106 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
107 /// let api_key = &ctx.state().api_key;
108 /// // Use the API key for authentication
109 /// }
110 /// ```
111 pub fn state(&self) -> &State {
112 &self.state
113 }
114
115 /// Returns a reference to the original Lambda event.
116 ///
117 /// This provides access to the raw event data from AWS Lambda, which can be useful
118 /// for accessing event-specific fields not exposed through the router interface.
119 ///
120 /// # Examples
121 ///
122 /// ```rust
123 /// # use lambda_lw_http_router_core::{RouteContext, Router};
124 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
125 /// # #[derive(Clone)]
126 /// # struct AppState {}
127 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
128 /// let raw_event = ctx.event();
129 /// if let Some(body) = &raw_event.body {
130 /// // Process the raw request body
131 /// }
132 /// }
133 /// ```
134 pub fn event(&self) -> &E {
135 &self.event
136 }
137
138 /// Returns a reference to the Lambda execution context.
139 ///
140 /// The Lambda context contains metadata about the current execution environment,
141 /// such as the request ID, function name, and remaining execution time.
142 ///
143 /// # Examples
144 ///
145 /// ```rust
146 /// # use lambda_lw_http_router_core::{RouteContext, Router};
147 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
148 /// # #[derive(Clone)]
149 /// # struct AppState {}
150 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
151 /// let request_id = &ctx.lambda_context().request_id;
152 /// let deadline = ctx.lambda_context().deadline;
153 /// }
154 /// ```
155 pub fn lambda_context(&self) -> &lambda_runtime::Context {
156 &self.lambda_context
157 }
158
159 /// Returns the route pattern that matched this request.
160 ///
161 /// The route pattern is the original path template with parameter placeholders,
162 /// such as "/users/{id}/profile".
163 ///
164 /// # Examples
165 ///
166 /// ```rust
167 /// # use lambda_lw_http_router_core::{RouteContext, Router};
168 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
169 /// # #[derive(Clone)]
170 /// # struct AppState {}
171 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
172 /// let pattern = ctx.route_pattern();
173 /// assert_eq!(pattern, "/users/{id}/profile");
174 /// }
175 /// ```
176 pub fn route_pattern(&self) -> &str {
177 &self.route_pattern
178 }
179
180 /// Returns a path parameter by name, if it exists.
181 ///
182 /// Path parameters are extracted from the URL based on the route pattern.
183 /// For example, if the route pattern is "/users/{id}" and the URL is "/users/123",
184 /// then `get_param("id")` will return `Some("123")`.
185 ///
186 /// # Arguments
187 ///
188 /// * `name` - The name of the path parameter to retrieve
189 ///
190 /// # Examples
191 ///
192 /// ```rust
193 /// # use lambda_lw_http_router_core::{RouteContext, Router};
194 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
195 /// # use serde_json::json;
196 /// # #[derive(Clone)]
197 /// # struct AppState {}
198 /// async fn get_user(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) -> serde_json::Value {
199 /// let user_id = ctx.get_param("id").unwrap_or_default();
200 /// json!({ "id": user_id })
201 /// }
202 /// ```
203 pub fn get_param(&self, name: &str) -> Option<String> {
204 self.params.get(name).cloned()
205 }
206
207 /// Returns a path parameter by name, or a default value if it doesn't exist.
208 ///
209 /// # Examples
210 ///
211 /// ```rust
212 /// # use lambda_lw_http_router_core::{RouteContext, Router};
213 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
214 /// # use serde_json::json;
215 /// # #[derive(Clone)]
216 /// # struct AppState {}
217 /// async fn get_user(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) -> serde_json::Value {
218 /// let user_id = ctx.get_param_or("id", "default");
219 /// json!({ "id": user_id })
220 /// }
221 /// ```
222 pub fn get_param_or(&self, name: &str, default: &str) -> String {
223 self.get_param(name).unwrap_or_else(|| default.to_string())
224 }
225
226 /// Returns a path parameter by name, or an empty string if it doesn't exist.
227 ///
228 /// # Examples
229 ///
230 /// ```rust
231 /// # use lambda_lw_http_router_core::{RouteContext, Router};
232 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
233 /// # use serde_json::json;
234 /// # #[derive(Clone)]
235 /// # struct AppState {}
236 /// async fn get_user(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) -> serde_json::Value {
237 /// let user_id = ctx.get_param_or_empty("id");
238 /// json!({ "id": user_id })
239 /// }
240 /// ```
241 pub fn get_param_or_empty(&self, name: &str) -> String {
242 self.get_param_or(name, "")
243 }
244
245 /// Returns a reference to all path parameters.
246 ///
247 /// This method returns a HashMap containing all path parameters extracted from
248 /// the URL based on the route pattern.
249 ///
250 /// # Examples
251 ///
252 /// ```rust
253 /// # use lambda_lw_http_router_core::{RouteContext, Router};
254 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
255 /// # use serde_json::json;
256 /// # #[derive(Clone)]
257 /// # struct AppState {}
258 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) -> serde_json::Value {
259 /// let params = ctx.params();
260 /// json!({
261 /// "user_id": params.get("user_id"),
262 /// "post_id": params.get("post_id")
263 /// })
264 /// }
265 /// ```
266 pub fn params(&self) -> &HashMap<String, String> {
267 &self.params
268 }
269
270 /// Sets a single attribute on the current OpenTelemetry span.
271 ///
272 /// This method allows you to add custom attributes to the current span for
273 /// better observability and tracing.
274 ///
275 /// # Arguments
276 ///
277 /// * `key` - The attribute key
278 /// * `value` - The attribute value (supports strings, numbers, and booleans)
279 ///
280 /// # Returns
281 ///
282 /// Returns a reference to self for method chaining
283 ///
284 /// # Examples
285 ///
286 /// ```rust
287 /// # use lambda_lw_http_router_core::{RouteContext, Router};
288 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
289 /// # #[derive(Clone)]
290 /// # struct AppState {}
291 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
292 /// ctx.set_otel_attribute("user.id", "123")
293 /// .set_otel_attribute("request.size", 1024)
294 /// .set_otel_attribute("cache.hit", true);
295 /// }
296 /// ```
297 pub fn set_otel_attribute(
298 &self,
299 key: impl Into<OtelKey>,
300 value: impl Into<OtelValue>,
301 ) -> &Self {
302 let span = Span::current();
303 span.set_attribute(key, value);
304 self
305 }
306
307 /// Sets the OpenTelemetry span kind for the current span.
308 ///
309 /// The span kind describes the relationship between the span and its parent.
310 /// Common values include:
311 /// - "SERVER" for server-side request handling
312 /// - "CLIENT" for outbound requests
313 /// - "PRODUCER" for message publishing
314 /// - "CONSUMER" for message processing
315 /// - "INTERNAL" for internal operations
316 ///
317 /// # Arguments
318 ///
319 /// * `kind` - The span kind to set
320 ///
321 /// # Returns
322 ///
323 /// Returns a reference to self for method chaining
324 ///
325 /// # Examples
326 ///
327 /// ```rust
328 /// # use lambda_lw_http_router_core::{RouteContext, Router};
329 /// # use aws_lambda_events::apigw::ApiGatewayV2httpRequest;
330 /// # #[derive(Clone)]
331 /// # struct AppState {}
332 /// async fn handler(ctx: RouteContext<AppState, ApiGatewayV2httpRequest>) {
333 /// ctx.set_otel_span_kind("SERVER")
334 /// .set_otel_attribute("request.id", "abc-123");
335 /// }
336 /// ```
337 pub fn set_otel_span_kind(&self, kind: &str) -> &Self {
338 let span = Span::current();
339 span.record("otel.kind", kind);
340 self
341 }
342}