use crate::auth::{AuthData, Authorization};
use crate::XSpanIdString;
use hyper::{service::Service, Request, Response};
use std::future::Future;
use std::pin::Pin;
pub trait Has<T> {
fn get(&self) -> &T;
fn get_mut(&mut self) -> &mut T;
fn set(&mut self, value: T);
}
pub trait Pop<T> {
type Result;
fn pop(self) -> (T, Self::Result);
}
pub trait Push<T> {
type Result;
fn push(self, value: T) -> Self::Result;
}
#[macro_export]
macro_rules! new_context_type {
($context_name:ident, $empty_context_name:ident, $($types:ty),+ ) => {
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct $context_name<T, C> {
head: T,
tail: C,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct $empty_context_name;
// implement `Push<T>` on the empty context type for each type `T` that
$(
impl $crate::Push<$types> for $empty_context_name {
type Result = $context_name<$types, Self>;
fn push(self, item: $types) -> Self::Result {
$context_name{head: item, tail: Self::default()}
}
}
impl<C> $crate::Has<$types> for $context_name<$types, C> {
fn set(&mut self, item: $types) {
self.head = item;
}
fn get(&self) -> &$types {
&self.head
}
fn get_mut(&mut self) -> &mut $types {
&mut self.head
}
}
impl<C> $crate::Pop<$types> for $context_name<$types, C> {
type Result = C;
fn pop(self) -> ($types, Self::Result) {
(self.head, self.tail)
}
}
impl<C, T> $crate::Push<$types> for $context_name<T, C> {
type Result = $context_name<$types, Self>;
fn push(self, item: $types) -> Self::Result {
$context_name{head: item, tail: self}
}
}
)+
$crate::new_context_type!(impl extend_has $context_name, $empty_context_name, $($types),+);
};
(impl extend_has $context_name:ident, $empty_context_name:ident, $head:ty, $($tail:ty),+ ) => {
$crate::new_context_type!(
impl extend_has_helper
$context_name,
$empty_context_name,
$head,
$($tail),+
);
$crate::new_context_type!(impl extend_has $context_name, $empty_context_name, $($tail),+);
};
(impl extend_has $context_name:ident, $empty_context_name:ident, $head:ty) => {};
(impl extend_has_helper
$context_name:ident,
$empty_context_name:ident,
$type:ty,
$($types:ty),+
) => {
$(
impl<C: $crate::Has<$type>> $crate::Has<$type> for $context_name<$types, C> {
fn set(&mut self, item: $type) {
self.tail.set(item);
}
fn get(&self) -> &$type {
self.tail.get()
}
fn get_mut(&mut self) -> &mut $type {
self.tail.get_mut()
}
}
impl<C: $crate::Has<$types>> $crate::Has<$types> for $context_name<$type, C> {
fn set(&mut self, item: $types) {
self.tail.set(item);
}
fn get(&self) -> &$types {
self.tail.get()
}
fn get_mut(&mut self) -> &mut $types {
self.tail.get_mut()
}
}
impl<C> $crate::Pop<$type> for $context_name<$types, C> where C: $crate::Pop<$type> {
type Result = $context_name<$types, C::Result>;
fn pop(self) -> ($type, Self::Result) {
let (value, tail) = self.tail.pop();
(value, $context_name{ head: self.head, tail})
}
}
impl<C> $crate::Pop<$types> for $context_name<$type, C> where C: $crate::Pop<$types> {
type Result = $context_name<$type, C::Result>;
fn pop(self) -> ($types, Self::Result) {
let (value, tail) = self.tail.pop();
(value, $context_name{ head: self.head, tail})
}
}
)+
};
}
new_context_type!(
ContextBuilder,
EmptyContext,
XSpanIdString,
Option<AuthData>,
Option<Authorization>
);
#[macro_export]
macro_rules! make_context_ty {
($context_name:ident, $empty_context_name:ident, $type:ty $(, $types:ty)* $(,)* ) => {
$context_name<$type, $crate::make_context_ty!($context_name, $empty_context_name, $($types),*)>
};
($context_name:ident, $empty_context_name:ident $(,)* ) => {
$empty_context_name
};
}
#[macro_export]
macro_rules! make_context {
($context_name:ident, $empty_context_name:ident, $value:expr $(, $values:expr)* $(,)*) => {
$crate::make_context!($context_name, $empty_context_name, $($values),*).push($value)
};
($context_name:ident, $empty_context_name:ident $(,)* ) => {
$empty_context_name::default()
};
}
#[derive(Debug)]
pub struct ContextWrapper<T, C> {
api: T,
context: C,
}
impl<T, C> ContextWrapper<T, C> {
pub fn new(api: T, context: C) -> Self {
Self { api, context }
}
pub fn api(&self) -> &T {
&self.api
}
pub fn context(&self) -> &C {
&self.context
}
}
impl<T: Clone, C: Clone> Clone for ContextWrapper<T, C> {
fn clone(&self) -> Self {
ContextWrapper {
api: self.api.clone(),
context: self.context.clone(),
}
}
}
pub trait SwaggerService<RequestBody, ResponseBody, Context>:
Clone
+ Service<
(Request<RequestBody>, Context),
Response = Response<ResponseBody>,
Error = hyper::Error,
Future = Pin<Box<dyn Future<Output = Result<Response<ResponseBody>, hyper::Error>>>>,
>
where
Context: Has<Option<AuthData>>
+ Has<Option<Authorization>>
+ Has<XSpanIdString>
+ Clone
+ 'static
+ Send,
{
}
impl<ReqB, ResB, Context, T> SwaggerService<ReqB, ResB, Context> for T
where
T: Clone
+ Service<
(Request<ReqB>, Context),
Response = Response<ResB>,
Error = hyper::Error,
Future = Pin<Box<dyn Future<Output = Result<Response<ResB>, hyper::Error>>>>,
>,
Context: Has<Option<AuthData>>
+ Has<Option<Authorization>>
+ Has<XSpanIdString>
+ Clone
+ 'static
+ Send,
{
}
#[cfg(test)]
mod context_tests {
use super::Has;
use super::*;
struct ContextItem1 {
val: u32,
}
struct ContextItem2;
struct ContextItem3;
new_context_type!(
MyContext,
MyEmptyContext,
ContextItem1,
ContextItem2,
ContextItem3
);
#[test]
fn send_request() {
let t = MyEmptyContext::default();
let t = t.push(ContextItem1 { val: 1 });
let t = t.push(ContextItem2);
{
let v: &ContextItem1 = t.get();
assert_eq!(v.val, 1);
}
let (_, mut t): (ContextItem2, _) = t.pop();
{
let v: &mut ContextItem1 = t.get_mut();
v.val = 4;
}
{
let v: &ContextItem1 = t.get();
assert_eq!(v.val, 4);
}
}
}