use std::marker::PhantomData;
use super::{HttpMarker, ModelMarker, Plugin};
pub struct True;
pub struct False;
pub trait ConditionalApply<Ser, Op, Pl, T> {
type Service;
fn apply(plugin: &Pl, svc: T) -> Self::Service;
}
impl<Ser, Op, Pl, T> ConditionalApply<Ser, Op, Pl, T> for True
where
Pl: Plugin<Ser, Op, T>,
{
type Service = Pl::Output;
fn apply(plugin: &Pl, input: T) -> Self::Service {
plugin.apply(input)
}
}
impl<P, Op, Pl, T> ConditionalApply<P, Op, Pl, T> for False {
type Service = T;
fn apply(_plugin: &Pl, input: T) -> Self::Service {
input
}
}
pub struct Scoped<Scope, Pl> {
scope: PhantomData<Scope>,
plugin: Pl,
}
impl<Pl> Scoped<(), Pl> {
pub fn new<Scope>(plugin: Pl) -> Scoped<Scope, Pl> {
Scoped {
scope: PhantomData,
plugin,
}
}
}
pub trait Membership<Op> {
type Contains;
}
impl<Ser, Op, T, Scope, Pl> Plugin<Ser, Op, T> for Scoped<Scope, Pl>
where
Scope: Membership<Op>,
Scope::Contains: ConditionalApply<Ser, Op, Pl, T>,
{
type Output = <Scope::Contains as ConditionalApply<Ser, Op, Pl, T>>::Service;
fn apply(&self, input: T) -> Self::Output {
<Scope::Contains as ConditionalApply<Ser, Op, Pl, T>>::apply(&self.plugin, input)
}
}
impl<Scope, Pl> HttpMarker for Scoped<Scope, Pl> where Pl: HttpMarker {}
impl<Scope, Pl> ModelMarker for Scoped<Scope, Pl> where Pl: ModelMarker {}
#[macro_export]
macro_rules! scope {
(
$(#[$attrs:meta])*
$vis:vis struct $name:ident {
includes: [$($include:ty),*],
excludes: [$($exclude:ty),*]
}
) => {
$(#[$attrs])*
$vis struct $name;
$(
impl $crate::plugin::scoped::Membership<$include> for $name {
type Contains = $crate::plugin::scoped::True;
}
)*
$(
impl $crate::plugin::scoped::Membership<$exclude> for $name {
type Contains = $crate::plugin::scoped::False;
}
)*
};
}
#[cfg(test)]
mod tests {
use crate::plugin::Plugin;
use super::Scoped;
struct OperationA;
struct OperationB;
scope! {
pub struct AuthScope {
includes: [OperationA],
excludes: [OperationB]
}
}
struct MockPlugin;
impl<P, Op> Plugin<P, Op, u32> for MockPlugin {
type Output = String;
fn apply(&self, svc: u32) -> Self::Output {
svc.to_string()
}
}
#[test]
fn scope() {
let plugin = MockPlugin;
let scoped_plugin = Scoped::new::<AuthScope>(plugin);
let out: String = Plugin::<(), OperationA, _>::apply(&scoped_plugin, 3_u32);
assert_eq!(out, "3".to_string());
let out: u32 = Plugin::<(), OperationB, _>::apply(&scoped_plugin, 3_u32);
assert_eq!(out, 3);
}
}