use auth::{Authorization, AuthData};
use std::marker::Sized;
use super::XSpanIdString;
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, 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 any `T`, so that
impl<U> $crate::Push<U> for $empty_context_name {
type Result = $context_name<U, Self>;
fn push(self, item: U) -> Self::Result {
$context_name{head: item, tail: Self::default()}
}
}
impl<T, C> $crate::Has<T> for $context_name<T, C> {
fn set(&mut self, item: T) {
self.head = item;
}
fn get(&self) -> &T {
&self.head
}
fn get_mut(&mut self) -> &mut T {
&mut self.head
}
}
impl<T, C> $crate::Pop<T> for $context_name<T, C> {
type Result = C;
fn pop(self) -> (T, Self::Result) {
(self.head, self.tail)
}
}
impl<C, T, U> $crate::Push<U> for $context_name<T, C> {
type Result = $context_name<U, Self>;
fn push(self, item: U) -> Self::Result {
$context_name{head: item, tail: self}
}
}
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),+ ) => {
new_context_type!(
impl extend_has_helper
$context_name,
$empty_context_name,
$head,
$($tail),+
);
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: 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: 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, 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)* $(,)*) => {
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: 'a, 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
}
}
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)
}
}
#[cfg(test)]
mod context_tests {
use hyper::server::{NewService, Service};
use hyper::{Response, Request, Error, Method, Uri};
use std::marker::PhantomData;
use std::io;
use std::str::FromStr;
use futures::future::{Future, ok};
use super::*;
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>,
{
type Request = (Request, C);
type Response = Response;
type Error = Error;
type Future = Box<Future<Item = Response, Error = Error>>;
fn call(&self, (_, context): Self::Request) -> Self::Future {
use_item_2(Has::<ContextItem2>::get(&context));
let (item3, _): (ContextItem3, _) = context.pop();
use_item_3_owned(item3);
Box::new(ok(Response::new()))
}
}
struct InnerNewService<C>
where
C: Has<ContextItem2> + Pop<ContextItem3>,
{
marker: PhantomData<C>,
}
impl<C> InnerNewService<C>
where
C: Has<ContextItem2> + Pop<ContextItem3>,
{
fn new() -> Self {
InnerNewService { marker: PhantomData }
}
}
impl<C> NewService for InnerNewService<C>
where
C: Has<ContextItem2> + Pop<ContextItem3>,
{
type Request = (Request, C);
type Response = Response;
type Error = Error;
type Instance = InnerService<C>;
fn new_service(&self) -> Result<Self::Instance, io::Error> {
Ok(InnerService { marker: PhantomData })
}
}
struct MiddleService<T, C>
where
C: Pop<ContextItem1>,
C::Result: Push<ContextItem2>,
<C::Result as Push<ContextItem2>>::Result: Push<ContextItem3>,
T: Service<Request=(
Request,
<<C::Result as Push<ContextItem2>>::Result as Push<ContextItem3>>::Result
)>,
{
inner: T,
marker1: PhantomData<C>,
}
impl<T, C, D, E> Service for MiddleService<T, C>
where
C: Pop<ContextItem1, Result = D>,
D: Push<ContextItem2, Result = E>,
E: Push<ContextItem3>,
T: Service<Request = (Request, E::Result)>,
{
type Request = (Request, C);
type Response = T::Response;
type Error = T::Error;
type Future = T::Future;
fn call(&self, (req, context): Self::Request) -> Self::Future {
let (item, context) = context.pop();
use_item_1_owned(item);
let context = context.push(ContextItem2 {}).push(ContextItem3 {});
self.inner.call((req, context))
}
}
struct MiddleNewService<T, C>
where
C: Pop<ContextItem1>,
C::Result: Push<ContextItem2>,
<C::Result as Push<ContextItem2>>::Result: Push<ContextItem3>,
T: NewService<Request=(
Request,
<<C::Result as Push<ContextItem2>>::Result as Push<ContextItem3>>::Result
)>,
{
inner: T,
marker1: PhantomData<C>,
}
impl<T, C, D, E> NewService for MiddleNewService<T, C>
where
C: Pop<ContextItem1, Result = D>,
D: Push<ContextItem2, Result = E>,
E: Push<ContextItem3>,
T: NewService<Request = (Request, E::Result)>,
{
type Request = (Request, C);
type Response = T::Response;
type Error = T::Error;
type Instance = MiddleService<T::Instance, C>;
fn new_service(&self) -> Result<Self::Instance, io::Error> {
self.inner.new_service().map(|s| {
MiddleService {
inner: s,
marker1: PhantomData,
}
})
}
}
impl<T, C, D, E> MiddleNewService<T, C>
where
C: Pop<ContextItem1, Result = D>,
D: Push<ContextItem2, Result = E>,
E: Push<ContextItem3>,
T: NewService<Request = (Request, E::Result)>,
{
fn new(inner: T) -> Self {
MiddleNewService {
inner,
marker1: PhantomData,
}
}
}
struct OuterService<T, C>
where
C: Default + Push<ContextItem1>,
T: Service<Request = (Request, C::Result)>,
{
inner: T,
marker: PhantomData<C>,
}
impl<T, C> Service for OuterService<T, C>
where
C: Default + Push<ContextItem1>,
T: Service<Request = (Request, C::Result)>,
{
type Request = Request;
type Response = T::Response;
type Error = T::Error;
type Future = T::Future;
fn call(&self, req: Self::Request) -> Self::Future {
let context = C::default().push(ContextItem1 {});
self.inner.call((req, context))
}
}
struct OuterNewService<T, C>
where
C: Default + Push<ContextItem1>,
T: NewService<Request = (Request, C::Result)>,
{
inner: T,
marker: PhantomData<C>,
}
impl<T, C> NewService for OuterNewService<T, C>
where
C: Default + Push<ContextItem1>,
T: NewService<Request = (Request, C::Result)>,
{
type Request = Request;
type Response = T::Response;
type Error = T::Error;
type Instance = OuterService<T::Instance, C>;
fn new_service(&self) -> Result<Self::Instance, io::Error> {
self.inner.new_service().map(|s| {
OuterService {
inner: s,
marker: PhantomData,
}
})
}
}
impl<T, C> OuterNewService<T, C>
where
C: Default + Push<ContextItem1>,
T: NewService<Request = (Request, C::Result)>,
{
fn new(inner: T) -> Self {
OuterNewService {
inner,
marker: PhantomData,
}
}
}
new_context_type!(MyContext, MyEmptyContext, ContextItem1, ContextItem2, ContextItem3);
#[test]
fn send_request() {
let new_service = OuterNewService::<_, MyEmptyContext>::new(
MiddleNewService::new(InnerNewService::new()),
);
let req = Request::new(Method::Post, Uri::from_str("127.0.0.1:80").unwrap());
new_service
.new_service()
.expect("Failed to start new service")
.call(req)
.wait()
.expect("Service::call returned an error");
}
}