use crate::auth::{AuthData, Authorization};
use crate::XSpanIdString;
use futures::future::Future;
use hyper;
use std::marker::Sized;
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<'a, T, C> {
api: &'a T,
context: C,
}
impl<'a, T, C> ContextWrapper<'a, T, C> {
pub fn new(api: &'a T, context: C) -> ContextWrapper<'a, T, C> {
ContextWrapper { api, context }
}
pub fn api(&self) -> &T {
self.api
}
pub fn context(&self) -> &C {
&self.context
}
}
impl<'a, T, C: Clone> Clone for ContextWrapper<'a, T, C> {
fn clone(&self) -> Self {
ContextWrapper {
api: self.api,
context: self.context.clone(),
}
}
}
pub trait ContextWrapperExt<'a, C>
where
Self: Sized,
{
fn with_context(self: &'a Self, context: C) -> ContextWrapper<'a, Self, C> {
ContextWrapper::<Self, C>::new(self, context)
}
}
pub trait SwaggerService<C>:
Clone
+ hyper::service::Service<
ReqBody = ContextualPayload<hyper::Body, C>,
ResBody = hyper::Body,
Error = hyper::Error,
Future = Box<dyn Future<Item = hyper::Response<hyper::Body>, Error = hyper::Error> + Send>,
>
where
C: Has<Option<AuthData>>
+ Has<Option<Authorization>>
+ Has<XSpanIdString>
+ Clone
+ 'static
+ Send,
{
}
impl<T, C> SwaggerService<C> for T
where
T: Clone
+ hyper::service::Service<
ReqBody = ContextualPayload<hyper::Body, C>,
ResBody = hyper::Body,
Error = hyper::Error,
Future = Box<
dyn Future<Item = hyper::Response<hyper::Body>, Error = hyper::Error> + Send,
>,
>,
C: Has<Option<AuthData>>
+ Has<Option<Authorization>>
+ Has<XSpanIdString>
+ Clone
+ 'static
+ Send,
{
}
#[derive(Clone, Debug)]
pub struct ContextualPayload<P, Ctx>
where
P: hyper::body::Payload,
Ctx: Send + 'static,
{
pub inner: P,
pub context: Ctx,
}
impl<P, Ctx> hyper::body::Payload for ContextualPayload<P, Ctx>
where
P: hyper::body::Payload,
Ctx: Send + 'static,
{
type Data = P::Data;
type Error = P::Error;
fn poll_data(&mut self) -> futures::Poll<Option<Self::Data>, Self::Error> {
self.inner.poll_data()
}
}
#[cfg(test)]
mod context_tests {
use super::*;
use futures::future::{ok, Future, FutureResult};
use hyper::service::{MakeService, Service};
use hyper::{Body, Error, Method, Request, Response, Uri};
use std::io;
use std::marker::PhantomData;
use std::str::FromStr;
struct ContextItem1;
struct ContextItem2;
struct ContextItem3;
fn use_item_1_owned(_: ContextItem1) {}
fn use_item_2(_: &ContextItem2) {}
fn use_item_3_owned(_: ContextItem3) {}
struct InnerService<C>
where
C: Has<ContextItem2> + Pop<ContextItem3>,
{
marker: PhantomData<C>,
}
impl<C> Service for InnerService<C>
where
C: Has<ContextItem2> + Pop<ContextItem3> + Send + 'static,
{
type ReqBody = ContextualPayload<Body, C>;
type ResBody = Body;
type Error = Error;
type Future = Box<dyn Future<Item = Response<Body>, Error = Error>>;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
use_item_2(Has::<ContextItem2>::get(&req.body().context));
let (_, body) = req.into_parts();
let (item3, _): (ContextItem3, _) = body.context.pop();
use_item_3_owned(item3);
Box::new(ok(Response::new(Body::empty())))
}
}
struct InnerMakeService<RC>
where
RC: Has<ContextItem2> + Pop<ContextItem3>,
{
marker: PhantomData<RC>,
}
impl<RC> InnerMakeService<RC>
where
RC: Has<ContextItem2> + Pop<ContextItem3>,
{
fn new() -> Self {
InnerMakeService {
marker: PhantomData,
}
}
}
impl<RC, SC> MakeService<SC> for InnerMakeService<RC>
where
RC: Has<ContextItem2> + Pop<ContextItem3> + Send + 'static,
{
type ReqBody = ContextualPayload<Body, RC>;
type ResBody = Body;
type Error = Error;
type Service = InnerService<RC>;
type Future = FutureResult<Self::Service, Self::MakeError>;
type MakeError = io::Error;
fn make_service(&mut self, _: SC) -> FutureResult<Self::Service, io::Error> {
ok(InnerService {
marker: PhantomData,
})
}
}
struct MiddleService<T, RC>
where
RC: Pop<ContextItem1>,
RC::Result: Push<ContextItem2>,
<RC::Result as Push<ContextItem2>>::Result: Push<ContextItem3>,
<<RC::Result as Push<ContextItem2>>::Result as Push<ContextItem3>>::Result: Send + 'static,
T: Service<
ReqBody = ContextualPayload<
Body,
<<RC::Result as Push<ContextItem2>>::Result as Push<ContextItem3>>::Result,
>,
>,
{
inner: T,
marker1: PhantomData<RC>,
}
impl<T, C, D, E> Service for MiddleService<T, C>
where
C: Pop<ContextItem1, Result = D> + Send + 'static,
D: Push<ContextItem2, Result = E>,
E: Push<ContextItem3>,
T: Service<ReqBody = ContextualPayload<Body, E::Result>>,
E::Result: Send + 'static,
{
type ReqBody = ContextualPayload<Body, C>;
type ResBody = T::ResBody;
type Error = T::Error;
type Future = T::Future;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
let (head, body) = req.into_parts();
let (item, context) = body.context.pop();
use_item_1_owned(item);
let context = context.push(ContextItem2 {}).push(ContextItem3 {});
let req = Request::from_parts(
head,
ContextualPayload {
inner: body.inner,
context,
},
);
self.inner.call(req)
}
}
struct MiddleMakeService<T, SC, RC>
where
RC: Pop<ContextItem1>,
RC::Result: Push<ContextItem2>,
<RC::Result as Push<ContextItem2>>::Result: Push<ContextItem3>,
<<RC::Result as Push<ContextItem2>>::Result as Push<ContextItem3>>::Result: Send + 'static,
T: MakeService<
SC,
ReqBody = ContextualPayload<
Body,
<<RC::Result as Push<ContextItem2>>::Result as Push<ContextItem3>>::Result,
>,
>,
{
inner: T,
marker1: PhantomData<RC>,
marker2: PhantomData<SC>,
}
impl<T, SC, RC, D, E> MakeService<SC> for MiddleMakeService<T, SC, RC>
where
RC: Pop<ContextItem1, Result = D> + Send + 'static,
D: Push<ContextItem2, Result = E>,
E: Push<ContextItem3>,
T: MakeService<SC, ReqBody = ContextualPayload<Body, E::Result>>,
T::Future: 'static,
E::Result: Send + 'static,
{
type ReqBody = ContextualPayload<Body, RC>;
type ResBody = T::ResBody;
type Error = T::Error;
type Service = MiddleService<T::Service, RC>;
type Future = Box<dyn Future<Item = Self::Service, Error = Self::MakeError>>;
type MakeError = T::MakeError;
fn make_service(&mut self, sc: SC) -> Self::Future {
Box::new(self.inner.make_service(sc).map(|s| MiddleService {
inner: s,
marker1: PhantomData,
}))
}
}
impl<T, SC, RC, D, E> MiddleMakeService<T, SC, RC>
where
RC: Pop<ContextItem1, Result = D>,
D: Push<ContextItem2, Result = E>,
E: Push<ContextItem3>,
T: MakeService<SC, ReqBody = ContextualPayload<Body, E::Result>>,
E::Result: Send + 'static,
{
fn new(inner: T) -> Self {
MiddleMakeService {
inner,
marker1: PhantomData,
marker2: PhantomData,
}
}
}
struct OuterService<T, C>
where
C: Default + Push<ContextItem1>,
T: Service<ReqBody = ContextualPayload<Body, C::Result>>,
C::Result: Send + 'static,
{
inner: T,
marker: PhantomData<C>,
}
impl<T, C> Service for OuterService<T, C>
where
C: Default + Push<ContextItem1>,
T: Service<ReqBody = ContextualPayload<Body, C::Result>>,
C::Result: Send + 'static,
{
type ReqBody = Body;
type ResBody = T::ResBody;
type Error = T::Error;
type Future = T::Future;
fn call(&mut self, req: Request<Self::ReqBody>) -> Self::Future {
let context = C::default().push(ContextItem1 {});
let (header, body) = req.into_parts();
let req = Request::from_parts(
header,
ContextualPayload {
inner: body,
context,
},
);
self.inner.call(req)
}
}
struct OuterMakeService<T, SC, RC>
where
RC: Default + Push<ContextItem1>,
T: MakeService<SC, ReqBody = ContextualPayload<Body, RC::Result>>,
RC::Result: Send + 'static,
{
inner: T,
marker1: PhantomData<RC>,
marker2: PhantomData<SC>,
}
impl<T, SC, RC> MakeService<SC> for OuterMakeService<T, SC, RC>
where
RC: Default + Push<ContextItem1>,
RC::Result: Send + 'static,
T: MakeService<SC, ReqBody = ContextualPayload<Body, RC::Result>>,
T::Future: 'static,
{
type ReqBody = Body;
type ResBody = T::ResBody;
type Error = T::Error;
type Service = OuterService<T::Service, RC>;
type Future = Box<dyn Future<Item = Self::Service, Error = Self::MakeError>>;
type MakeError = T::MakeError;
fn make_service(&mut self, sc: SC) -> Self::Future {
Box::new(self.inner.make_service(sc).map(|s| OuterService {
inner: s,
marker: PhantomData,
}))
}
}
impl<T, SC, RC> OuterMakeService<T, SC, RC>
where
RC: Default + Push<ContextItem1>,
RC::Result: Send + 'static,
T: MakeService<SC, ReqBody = ContextualPayload<Body, RC::Result>>,
{
fn new(inner: T) -> Self {
OuterMakeService {
inner,
marker1: PhantomData,
marker2: PhantomData,
}
}
}
new_context_type!(
MyContext,
MyEmptyContext,
ContextItem1,
ContextItem2,
ContextItem3
);
#[test]
fn send_request() {
let mut make_service = OuterMakeService::<_, _, MyEmptyContext>::new(
MiddleMakeService::new(InnerMakeService::new()),
);
let req = Request::builder()
.method(Method::POST)
.uri(Uri::from_str("127.0.0.1:80").unwrap())
.body(Body::empty());
make_service
.make_service(())
.wait()
.expect("Failed to start new service")
.call(req.unwrap())
.wait()
.expect("Service::call returned an error");
}
}