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}