#[macro_export]
macro_rules! conditional {
($enum:ident, $( $variant:ident => $op:expr ),+ $(,)?) => {
{
#[allow(non_snake_case)]
struct ConditionalOp<$($variant),+> {
$(
$variant: $variant,
)+
}
impl<Value, Out, $($variant),+> Op for ConditionalOp<$($variant),+>
where
$($variant: Op<Input=Value, Output=Out>),+,
Value: Send + Sync,
Out: Send + Sync,
{
type Input = $enum<Value>;
type Output = Out;
fn call(&self, input: Self::Input) -> impl std::future::Future<Output=Self::Output> + Send {
async move {
match input {
$(
$enum::$variant(val) => self.$variant.call(val).await
),+
}
}
}
}
ConditionalOp { $($variant: $op),+ }
}
};
}
#[macro_export]
macro_rules! try_conditional {
($enum:ident, $( $variant:ident => $op:expr ),+ $(,)?) => {
{
#[allow(non_snake_case)]
struct TryConditionalOp<$( $variant ),+> {
$( $variant: $variant ),+
}
impl<Value, Out, Err, $( $variant ),+> TryOp for TryConditionalOp<$( $variant ),+>
where
$( $variant: TryOp<Input=Value, Output=Out, Error=Err> ),+,
Value: Send + Sync,
Out: Send + Sync,
Err: Send + Sync,
{
type Input = $enum<Value>;
type Output = Out;
type Error = Err;
async fn try_call(&self, input: Self::Input) -> Result<Self::Output, Self::Error> {
match input {
$(
$enum::$variant(val) => self.$variant.try_call(val).await
),+
}
}
}
TryConditionalOp { $($variant: $op),+ }
}
};
}
#[cfg(test)]
mod tests {
use crate::pipeline::*;
#[tokio::test]
async fn test_conditional_op() {
enum ExampleEnum<T> {
Variant1(T),
Variant2(T),
}
let op1 = map(|x: i32| x + 1);
let op2 = map(|x: i32| x * 2);
let conditional = conditional!(ExampleEnum,
Variant1 => op1,
Variant2 => op2
);
let result1 = conditional.call(ExampleEnum::Variant1(2)).await;
assert_eq!(result1, 3);
let result2 = conditional.call(ExampleEnum::Variant2(3)).await;
assert_eq!(result2, 6);
}
#[tokio::test]
async fn test_try_conditional_op() {
enum ExampleEnum<T> {
Variant1(T),
Variant2(T),
}
let op1 = map(|x: i32| Ok::<_, String>(x + 1));
let op2 = map(|x: i32| Ok::<_, String>(x * 2));
let try_conditional = try_conditional!(ExampleEnum,
Variant1 => op1,
Variant2 => op2
);
let result1 = try_conditional.try_call(ExampleEnum::Variant1(2)).await;
assert_eq!(result1, Ok(3));
let result2 = try_conditional.try_call(ExampleEnum::Variant2(3)).await;
assert_eq!(result2, Ok(6));
}
}