use std::sync::Arc;
use axum::http::{HeaderMap, Method};
use bytes::Bytes;
use smallvec::SmallVec;
use smol_str::SmolStr;
use crate::core::engine::{FrozenDiContainer, RouteSpec};
use crate::session::Session;
use crate::web::tenant::TenantConfig;
pub type Claims = serde_json::Map<String, serde_json::Value>;
#[derive(Clone)]
pub struct RequestContext {
method: Method,
raw_path: SmolStr,
query: SmolStr,
params: SmallVec<[(SmolStr, SmolStr); 4]>,
headers: HeaderMap,
body: Bytes,
claims: Option<Arc<Claims>>,
session: Option<Arc<Session>>,
tenant: Option<std::sync::Arc<TenantConfig>>,
trace_id: [u8; 16],
span_id: [u8; 8],
parent_span_id: [u8; 8],
container: &'static FrozenDiContainer,
route_pattern: &'static str,
route_spec: Option<&'static RouteSpec>,
extensions: axum::http::Extensions,
}
impl RequestContext {
#[inline]
pub fn method(&self) -> &Method {
&self.method
}
#[inline]
pub fn path(&self) -> &str {
&self.raw_path
}
#[inline]
pub fn query_string(&self) -> Option<&str> {
if self.query.is_empty() {
None
} else {
Some(&self.query)
}
}
#[inline]
pub fn body(&self) -> &Bytes {
&self.body
}
#[inline]
pub fn trace_id(&self) -> [u8; 16] {
self.trace_id
}
#[inline]
pub fn span_id(&self) -> [u8; 8] {
self.span_id
}
#[inline]
pub fn parent_span_id(&self) -> Option<[u8; 8]> {
if self.parent_span_id == [0u8; 8] {
None
} else {
Some(self.parent_span_id)
}
}
#[inline]
pub fn claims(&self) -> Option<&Claims> {
self.claims.as_deref()
}
#[inline]
pub fn session(&self) -> Option<&Arc<Session>> {
self.session.as_ref()
}
#[inline]
pub fn tenant(&self) -> Option<&TenantConfig> {
self.tenant.as_deref()
}
#[inline]
pub fn header(&self, key: &str) -> Option<&str> {
self.headers.get(key).and_then(|v| v.to_str().ok())
}
#[inline]
pub fn param(&self, name: &str) -> Option<&str> {
self.params
.iter()
.find(|(k, _)| k == name)
.map(|(_, v)| v.as_str())
}
#[inline]
pub fn inject<T: Send + Sync + 'static>(&self) -> &'static T {
self.container.get::<T>()
}
#[inline]
pub fn try_inject<T: Send + Sync + 'static>(&self) -> Option<&'static T> {
self.container.try_get::<T>()
}
#[inline]
pub fn route(&self) -> &'static str {
self.route_pattern
}
#[inline]
pub fn route_spec(&self) -> Option<&'static RouteSpec> {
self.route_spec
}
pub fn traceparent(&self) -> String {
format!(
"00-{}-{}-01",
hex_encode_16(&self.trace_id),
hex_encode_8(&self.span_id)
)
}
pub fn trace_id_hex(&self) -> String {
hex_encode_16(&self.trace_id)
}
#[doc(hidden)]
#[inline]
pub(crate) fn __with_claims(mut self, claims: Option<Arc<Claims>>) -> Self {
self.claims = claims;
self
}
#[doc(hidden)]
#[inline]
pub(crate) fn __with_session(mut self, session: Option<Arc<Session>>) -> Self {
self.session = session;
self
}
#[doc(hidden)]
#[inline]
pub(crate) fn __with_tenant(mut self, tenant: Option<std::sync::Arc<TenantConfig>>) -> Self {
self.tenant = tenant;
self
}
#[doc(hidden)]
#[allow(clippy::too_many_arguments)]
pub fn __new(
method: Method,
raw_path: SmolStr,
query: SmolStr,
params: SmallVec<[(SmolStr, SmolStr); 4]>,
headers: HeaderMap,
body: Bytes,
trace_id: [u8; 16],
span_id: [u8; 8],
parent_span_id: [u8; 8],
container: &'static FrozenDiContainer,
route_pattern: &'static str,
route_spec: Option<&'static RouteSpec>,
) -> Self {
Self {
method,
raw_path,
query,
params,
headers,
body,
claims: None,
session: None,
tenant: None,
trace_id,
span_id,
parent_span_id,
container,
route_pattern,
route_spec,
extensions: axum::http::Extensions::new(),
}
}
#[inline]
pub fn extensions(&self) -> &axum::http::Extensions {
&self.extensions
}
#[inline]
pub fn extensions_mut(&mut self) -> &mut axum::http::Extensions {
&mut self.extensions
}
}
#[inline]
pub(crate) fn hex_encode_16(b: &[u8; 16]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}
#[inline]
pub(crate) fn hex_encode_8(b: &[u8; 8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}