use crate::{request::Request, response::Response, router::Router};
#[cfg(feature = "operation")]
#[cfg_attr(docsrs, doc(cfg(feature = "operation")))]
pub static OPERATION_ID_HEADER: &str = "Operation-Id";
#[derive(Default)]
pub enum State {
Before(Box<Request>),
After(Box<Response>),
#[default]
Empty,
}
impl State {
pub fn take(&mut self) -> Self {
std::mem::take(self)
}
pub fn take_request(&mut self) -> Option<Request> {
match std::mem::take(self) {
State::Before(r) => Some(*r),
_ => None,
}
}
pub fn take_request_unchecked(&mut self) -> Request {
match std::mem::take(self) {
State::Before(r) => *r,
_ => panic!("State::take_request_unchecked should be called only before the handler & when it is ensured that the request wasn't moved"),
}
}
pub fn take_response(&mut self) -> Option<Response> {
match std::mem::take(self) {
State::After(r) => Some(*r),
_ => None,
}
}
pub fn take_response_unchecked(&mut self) -> Response {
match std::mem::take(self) {
State::After(r) => *r,
_ => panic!("State::take_response_unchecked should be called only after the handler & when it is ensured that the response wasn't moved"),
}
}
pub fn request(&self) -> Option<&Request> {
match self {
State::Before(r) => Some(r),
_ => None,
}
}
pub fn request_mut(&mut self) -> Option<&Request> {
match self {
State::Before(r) => Some(r),
_ => None,
}
}
pub fn request_unchecked(&self) -> &Request {
match self {
State::Before(r) => r,
_ => panic!("State::request_unchecked should be called only before the handler & when it is ensured that the request wasn't moved"),
}
}
pub fn request_unchecked_mut(&mut self) -> &mut Request {
match self {
State::Before(r) => r,
_ => panic!("State::request_unchecked_mut should be called only before the handler & when it is ensured that the request wasn't moved"),
}
}
pub fn response(&self) -> Option<&Response> {
match self {
State::After(r) => Some(r),
_ => None,
}
}
pub fn response_mut(&mut self) -> Option<&mut Response> {
match self {
State::After(r) => Some(r),
_ => None,
}
}
pub fn response_unchecked(&self) -> &Response {
match self {
State::After(r) => r,
_ => panic!("State::response_unchecked should be called only before the handler & when it is ensured that the request wasn't moved"),
}
}
pub fn response_unchecked_mut(&mut self) -> &mut Response {
match self {
State::After(r) => r,
_ => panic!("State::response_unchecked_mut should be called only before the handler & when it is ensured that the request wasn't moved"),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum RouteId {
Id(u64),
Error(u16),
}
impl RouteId {
pub(crate) fn new(id: u64) -> Self {
RouteId::Id(id)
}
}
impl Default for RouteId {
fn default() -> Self {
RouteId::Error(404)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct HandlerMetadata {
pub route_id: RouteId,
pub name: Option<&'static str>,
}
impl HandlerMetadata {
pub(crate) fn not_found() -> Self {
HandlerMetadata {
route_id: Default::default(),
name: None,
}
}
pub(crate) fn not_allowed() -> Self {
HandlerMetadata {
route_id: RouteId::Error(405),
name: None,
}
}
}
pub struct HttpContext {
pub state: State,
#[cfg(feature = "operation")]
#[cfg_attr(docsrs, doc(cfg(feature = "operation")))]
pub operation_id: crate::http_context::operation::OperationId,
pub metadata: HandlerMetadata,
pub(crate) router: Option<Router>,
}
impl HttpContext {
pub(crate) fn new(request: Request, router: Router, metadata: HandlerMetadata) -> Self {
#[cfg(not(feature = "operation"))]
{
let state = State::Before(Box::new(request));
let router = Some(router);
HttpContext { state, metadata, router }
}
#[cfg(feature = "operation")]
{
use std::str::FromStr;
let mut request = request;
let operation_id = request
.headers()
.get(OPERATION_ID_HEADER)
.and_then(|h| h.to_str().ok())
.and_then(|op_id_str| operation::OperationId::from_str(op_id_str).ok())
.unwrap_or_else(operation::OperationId::new);
*request.operation_id_mut() = operation_id;
let state = State::Before(Box::new(request));
let router = Some(router);
HttpContext {
state,
operation_id,
metadata,
router,
}
}
}
pub fn clone_with_empty_state(&self) -> Self {
HttpContext {
state: State::Empty,
router: self.router.clone(),
metadata: self.metadata.clone(),
#[cfg(feature = "operation")]
operation_id: self.operation_id,
}
}
pub fn before(&mut self, request: Request) {
self.state = State::Before(Box::new(request))
}
pub fn after(&mut self, response: Response) {
self.state = State::After(Box::new(response))
}
}
#[cfg(feature = "operation")]
#[cfg_attr(docsrs, doc(cfg(feature = "operation")))]
pub mod operation {
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use std::{
fmt::{Debug, Display, Formatter},
str::FromStr,
};
use uuid::Uuid;
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd, Default)]
pub struct OperationId(Uuid);
impl OperationId {
pub fn new() -> OperationId {
OperationId(Uuid::new_v4())
}
pub fn to_u128(self) -> u128 {
self.0.as_u128()
}
}
impl Display for OperationId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0.as_hyphenated(), f)
}
}
impl Debug for OperationId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0.as_hyphenated(), f)
}
}
impl FromStr for OperationId {
type Err = uuid::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Uuid::parse_str(s).map(OperationId)
}
}
struct OperationIdVisitor;
impl<'de> Visitor<'de> for OperationIdVisitor {
type Value = OperationId;
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
formatter.write_str("Invalid operation id")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
v.parse::<OperationId>().map_err(serde::de::Error::custom)
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
v.parse::<OperationId>().map_err(serde::de::Error::custom)
}
}
impl<'de> Deserialize<'de> for OperationId {
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(OperationIdVisitor)
}
}
impl Serialize for OperationId {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_str(self.to_string().as_str())
}
}
}