yerevan 0.2.6

Small Rust crate that brings computation expressions idea from F# for help you to work easier with functors and monads.
Documentation
//! Module containing the [`crate::yer`] macro.

/// `yerevan.rs` macros for fancy-shmancy syntax for computation expressions.
///
/// Example:
/// ```rust
/// use yerevan::yer;
///
/// // Some simple user-defined structs for compuation expressions
/// struct SimpleBinder {}
/// impl SimpleBinder {
///     pub fn bind<T, U>(val: Option<T>, f: &dyn Fn(T) -> Option<U>) -> Option<U> {
///         match val {
///             Some(v) => f(v),
///             None => SimpleBinder::zero(),
///         }
///     }
///     pub fn ret<T>(val: T) -> Option<T> {
///         Some(val)
///     }
///     pub fn zero<T>() -> Option<T> {
///         None
///     }
/// }
///
/// struct Incrementer {}
/// impl Incrementer {
///     pub fn bind(val: i32, f: &dyn Fn(i32) -> i32) -> i32 {
///         f(val + 1)
///     }
///     pub fn ret(val: i32) -> i32 {
///         val
///     }
/// }
///
/// pub fn showcase(wrapped1: Option<i32>, wrapped2: Option<&str>) -> bool {
///     let from_macro = yer!(
///         SimpleBinder =>
///         let! unwrapped1 = wrapped1;
///         let! unwrapped2 = wrapped2;
///         let one = 1;
///         Incrementer =>
///         let! res = one + unwrapped1 + (unwrapped2.len() as i32);
///         ret res
///     );
///
///     let by_hand =
///         SimpleBinder::bind(
///             wrapped1, &|unwrapped1| {
///                 SimpleBinder::bind(
///                     wrapped2, &|unwrapped2| {
///                         let one = 1;
///                         SimpleBinder::ret(
///                             Incrementer::bind(
///                                 one + unwrapped1 + (unwrapped2.len() as i32), &|res| {
///                                     Incrementer::ret(res)
///                                 }
///                             )
///                         )
///                     }
///                 )
///             }
///         );
///
///     from_macro == by_hand // true
/// }
/// ```

#[allow(unused_macros)]
#[macro_export]
macro_rules! yer {
    // let!
    (
        $struct_name:ident =>
        let! $var_name:ident$(: $var_type:ty)? = $expression:expr;
        $($tail:tt)*
    ) => {
        $struct_name::bind($expression, &|$var_name $(: $var_type)?| {
            yer!($struct_name => $($tail)*)
        })
    };

    // do!
    (
        $struct_name:ident =>
        do! $expression:expr;
        $($tail:tt)*
    ) => {
        $struct_name::bind($expression, &|_| {
            yer!($struct_name => $($tail)*)
        })
    };

    // let
    (
        $struct_name:ident =>
        let $var_name:ident $(: $var_type:ty)? = $expression:expr;
        $($tail:tt)*
    ) => {
        {
            let $var_name $(: $var_type)? = $expression;
            (yer!($struct_name => $($tail)*))
        }
    };

    // do
    (
        $struct_name:ident =>
        do $expression:expr;
        $($tail:tt)*
    ) => {
        {
            $expression;
            yer!($struct_name => $($tail)*)
        }
    };

    // ret with generics
    ( $struct_name:ident => ret <$($gtype:ty),+> $expression:expr ) => {
        $struct_name::ret::<$($gtype),+>($expression)
    };
    // ret
    ( $struct_name:ident => ret $expression:expr ) => {
        $struct_name::ret($expression)
    };

    // ret!
    ( $struct_name:ident => ret! $expression:expr ) => {
        $struct_name::ret_from($expression)
    };
    // ret! with generics
    ( $struct_name:ident => ret! <$($gtype:ty),+> $expression:expr ) => {
        $struct_name::ret_from::<$($gtype),+>($expression)
    };

    // yield! as return (last yield)
    (
        $struct_name:ident =>
        yield! $expression:expr;
    ) => {
        $struct_name::ret_yield_from($expression)
    };

    // combined yield!
    (
        $struct_name:ident =>
        yield! $expression:expr;
        $($tail:tt)*
    ) => {
        $struct_name::combine(
            yer!($struct_name => $($tail)*),
            $struct_name::ret_yield_from($expression)
        )
    };

    // yield as return (last yield)
    (
        $struct_name:ident =>
        yield $expression:expr;
    ) => {
        $struct_name::ret_yield($expression)
    };

    // combined yield
    (
        $struct_name:ident =>
        yield $expression:expr;
        $($tail:tt)*
    ) => {
        $struct_name::combine(
            yer!($struct_name => $($tail)*),
            $struct_name::ret_yield($expression)
        )
    };

    // running CE with run method
    ( run $struct_name:ident => $($tail:tt)* ) => {
        $struct_name::run({yer!($struct_name => $($tail)*)})
    };
    ( $struct_name:ident >> $($tail:tt)* ) => {
        $struct_name::run({yer!($struct_name => $($tail)*)})
    };

    // changing the CE-functions provider type
    ( $previous_struct_name:ident => $struct_name:ident => $($tail:tt)* ) => {
        $previous_struct_name::ret(yer!($struct_name => $($tail)*))
    };
    ( $previous_struct_name:ident => $struct_name:ident! => $($tail:tt)* ) => {
        $previous_struct_name::ret_from(yer!($struct_name => $($tail)*))
    };

    // If-branching
    (
        $struct_name:ident =>
        if ($if_expr:expr) {
            $($if_block:tt)*
        } else zero;
        $($tail:tt)*
    ) => {
        {
            $struct_name::combine(
                yer!($struct_name => $($tail)*),
                if $if_expr {
                    yer!(
                        $struct_name =>
                        $($if_block)*
                    )
                }
                else { $struct_name::zero() }
            )
        }
    };

    (
        $struct_name:ident =>
        if ($if_expr:expr) {
            $($if_block:tt)*
        }
        $( else if ($else_if_expr:expr) {
            $($else_if_block:tt)*
        })*
        else {
            $($else_block:tt)*
        }
        $($tail:tt)*
    ) => {
        {
            $struct_name::combine(
                yer!($struct_name => $($tail)*),
                if $if_expr {
                    yer!(
                        $struct_name =>
                        $($if_block)*
                    )
                }
                $(else if $else_if_expr {
                    yer!($struct_name => $($else_if_block)*)
                })*
                else {
                    yer!($struct_name => $($else_block)*)
                }
            )
        }
    };

    // exit-point
    ( $struct_name:ident => ) => { };
}