// ABOUTME: Request ID middleware for correlation and distributed tracing
// ABOUTME: Generates unique request IDs and propagates them through request/response lifecycle
//
// SPDX-License-Identifier: MIT OR Apache-2.0
// Copyright (c) 2025 Pierre Fitness Intelligence
//! Request ID middleware for request correlation
//!
//! This middleware generates a unique UUID v4 for each incoming request,
//! adds it to the request extensions for use by handlers, and includes it
//! in the response headers for client-side correlation.
use axum::{extract::Request, middleware::Next, response::Response};
use http::HeaderValue;
use std::fmt::{self, Display, Formatter};
use tracing::Span;
use uuid::Uuid;
/// Request ID header name
pub const REQUEST_ID_HEADER: &str = "x-request-id";
/// Request ID middleware that generates and propagates correlation IDs
///
/// This middleware:
/// 1. Generates a unique UUID v4 for each request
/// 2. Adds the request ID to request extensions for handler access
/// 3. Records the request ID in the current tracing span
/// 4. Includes the request ID in the response header
///
/// # Example
///
/// ```rust,no_run
/// use axum::{Router, routing::get, middleware};
/// use pierre_mcp_server::middleware::request_id::request_id_middleware;
///
/// # async fn handler() -> &'static str { "" }
/// let app: Router<()> = Router::new()
/// .route("/", get(handler))
/// .layer(middleware::from_fn(request_id_middleware));
/// ```
pub async fn request_id_middleware(mut req: Request, next: Next) -> Response {
// Generate unique request ID
let request_id = Uuid::new_v4().to_string();
// Record request ID in current tracing span
let span = Span::current();
span.record("request_id", &request_id);
// Add to request extensions for handler access
req.extensions_mut().insert(RequestId(request_id.clone()));
// Process request
let mut response = next.run(req).await;
// Add request ID to response header
if let Ok(header_value) = HeaderValue::from_str(&request_id) {
response
.headers_mut()
.insert(REQUEST_ID_HEADER, header_value);
}
response
}
/// Request ID extractor for use in handlers
///
/// This can be extracted in any Axum handler to access the request ID
/// generated by the middleware.
///
/// # Example
///
/// ```rust,no_run
/// use axum::Extension;
/// use pierre_mcp_server::middleware::request_id::RequestId;
///
/// async fn handler(Extension(request_id): Extension<RequestId>) -> String {
/// format!("Request ID: {}", request_id.0)
/// }
/// ```
#[derive(Debug, Clone)]
pub struct RequestId(pub String);
impl RequestId {
/// Get the request ID as a string slice
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Display for RequestId {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}