#[macro_export]
macro_rules! chain {
($function: ident($($params: expr),*)) => {
$function($($params),*)
};
(|$($c_params: ident),*| $body: block($($expr: expr),*)) => {
(|$($c_params),*| $body)($($expr),*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*)) => {
(|$($c_params),*| $body)($($expr),*)
};
(move |$($c_params: ident),*| $body: block($($expr: expr),*)) => {
(move |$($c_params),*| $body)($($expr),*)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*)) => {
(move |$($c_params),*| $body)($($expr),*)
};
(&|$($c_params: ident),*| $body: block($(*$expr: expr),*)) => {
(&|$($c_params),*| $body)($($expr),*)
};
((&|$($c_params: ident),*| $body: expr)($($expr: expr),*)) => {
(&|$($c_params),*| $body)($($expr),*)
};
(& mut |$($c_params: ident),*| $body: block($($expr: expr),*)) => {
(& mut |$($c_params),*| $body)($($expr),*)
};
((& mut |$($c_params: ident),*| $body: expr)($($expr: expr),*)) => {
(|$($c_params),*| $body)($($expr),*)
};
($function: ident($($params: expr),*), $function2: ident) => {
$function2($function($($params),*))
};
($function: ident($($params: expr),*), |$c_params: ident| $body: expr) => {
(|$c_params| $body)($function($($params),*))
};
($function: ident($($params: expr),*), |$c_params: ident: $ty: ident| $body: expr) => {
(|$c_params: $ty| $body)($function($($params),*))
};
($function: ident($($params: expr),*), |$c_params: ident: & $ty: ident| $body: expr) => {
(|$c_params: $ty| $body)($function($($params),*))
};
($function: ident($($params: expr),*), |$c_params: ident: & mut $ty: ident| $body: tt) => {
(|$c_params: $ty| $body)($function($($params),*))
};
($function: ident($($params: expr),*), move |$c_params: ident| $body: tt) => {
(move |$c_params| $body)($function($($params),*))
};
($function: ident($($params: expr),*), move |$c_params: ident: $ty: ident| $body: tt) => {
(move |$c_params: $ty| $body)($function($($params),*))
};
($function: ident($($params: expr),*), move |$c_params: ident: & $ty: ident| $body: tt) => {
(move |$c_params: $ty| $body)($function($($params),*))
};
($function: ident($($params: expr),*), move |$c_params: ident: & mut $ty: ident| $body: tt) => {
(move |$c_params: $ty| $body)($function($($params),*))
};
($function: ident($($params: expr),*), $function2: ident, $($more_func: ident),+) => {
chain!($function2($function($($params),*)), $($more_func),*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), $func: ident) => {
$func((|$($c_params),*| $body)($($expr),*))
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), $func: ident) => {
$func((move |$($c_params),*| $body)($($expr),*))
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), |$($c2_params: ident),*| $body2: expr) => {
(|$($c2_params),*| $body2)((|$($c_params),*| $body)($($expr),*))
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr) => {
(|$c2_params| $body2)((move |$($c_params),*| $body)($($expr),*))
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), $func: ident) => {
$func((|$($c_params: $ty),*| $body)($($expr),*))
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($expr: expr), $func: ident) => {
$func((move |$($c_params: $ty),*| $body)($($expr),*))
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr) => {
(|$c2_params| $body2)((|$($c_params),*| $body)($($expr),*))
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr) => {
(move |$c2_params| $body2)((|$($c_params),*| $body)($($expr),*))
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr) => {
(move |$c2_params| $body2)((move |$($c_params),*| $body)($($expr),*))
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr) => {
(|$c2_params| $body2)((|$($c_params: $ty),*| $body)($($expr),*))
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr) => {
(|$c2_params| $body2)((move |$($c_params: $ty),*| $body)($($expr),*))
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr) => {
(move |$c2_params| $body2)((|$($c_params: $ty),*| $body)($($expr),*))
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr) => {
(move |$c2_params| $body2)((move |$($c_params: $ty),*| $body)($($expr),*))
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_params: ident: $ty: ty| $body2: expr) => {
(|$c2_params: $ty| $body2)((|$($c_params),*| $body)($($expr),*))
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_params: ident: $ty: ty| $body2: expr) => {
(|$c2_params: $ty| $body2)((move |$($c_params),*| $body)($($expr),*))
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_params: ident: $ty: ty| $body2: expr) => {
(move |$c2_params: $ty| $body2)((|$($c_params),*| $body)($($expr),*))
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_params: ident: $ty: ty| $body2: expr) => {
(move |$c2_params: $ty| $body2)((move |$($c_params),*| $body)($($expr),*))
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_params: ident: $ty2: ty| $body2: expr) => {
(|$c2_params: $ty2| $body2)((|$($c_params: $ty),*| $body)($($expr),*))
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_params: ident: $ty2: ty| $body2: expr) => {
(|$c2_params: $ty2| $body2)((move |$($c_params: $ty),*| $body)($($expr),*))
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_params: ident: $ty2: ty| $body2: expr) => {
(move |$c2_params: $ty2| $body2)((|$($c_params: $ty),*| $body)($($expr),*))
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_params: ident: $ty2: ty| $body2: expr) => {
(move |$c2_params: $ty2| $body2)((move |$($c_params: $ty),*| $body)($($expr),*))
};
($function: ident($($params: expr),*), |$c_params: ident| $body: expr, $($other: tt)*) => {
chain!((|$c_params| $body)($function($($params),*)), $($other)*)
};
($function: ident($($params: expr),*), move |$c_params: ident| $body: expr, $($other: tt)*) => {
chain!((move |$c_params| $body)($function($($params),*)), $($other)*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), $function: ident, $($other: tt)*) => {
chain!($function((|$c_params| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), $function: ident, $($other: tt)*) => {
chain!($function((move |$($c_params),*| $body)($($expr),*)), $($other)*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), |$($c2_params: ident),*| $body2: expr, $($other: tt)*) => {
chain!((|$($c_params),*| $body)($($expr),*), |$($c2_params),*| $body2)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), |$($c2_params: ident),*| $body2: expr, $($other: tt)*) => {
chain!((move |$($c_params),*| $body)($($expr),*), |$($c2_params),*| $body2)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((move |$c2_params| $body2)((|$($c_params),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$($c2_params: ident),*| $body2: expr, $($other: tt)*) => {
chain!((move |$($c_params),*| $body)($($expr),*), move |$($c2_params),*| $body2)
};
($function: ident($($params: expr),*), |$c_params: ident: $ty: ty| $body: expr, $($other: tt)*) => {
chain!((|$c_params: $ty| $body)($function($($params),*)), $($other)*)
};
($function: ident($($params: expr),*), move |$c_params: ident: $ty: ty| $body: expr, $($other: tt)*) => {
chain!((move |$c_params: $ty| $body)($function($($params),*)), $($other)*)
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), $function: ident, $($other: tt)*) => {
chain!($function((|$($c_params: $ty),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), $function: ident, $($other: tt)*) => {
chain!($function((|$c_params: $ty| $body)($($expr),*)), $($other)*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((|$c2_params| $body2)((|$($c_params),*| $body)($($expr),*)), $(other)*)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((|$c2_params| $body2)((move |$($c_params),*| $body)($($expr),*)), $(other)*)
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((|$c2_params| $body2)((|$($c_params: $ty),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((|$c2_params| $body2)((move |$($c_params: $ty),*| $body)($($expr),*)), $($other)*)
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((move |$c2_params| $body2)((|$($c_params: $ty),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_params: ident| $body2: expr, $($other: tt)*) => {
chain!((move |$c2_params| $body2)((move |$($c_params: $ty),*| $body)($($expr),*)), $($other)*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((|$sc2_param : $ty2| $body2)((|$($c_params),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((|$sc2_param : $ty2| $body2)((move |$($c_params),*| $body)($($expr),*)), $($other)*)
};
((|$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((move |$sc2_param : $ty2| $body2)((|$($c_params),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident),*| $body: expr)($($expr: expr),*), move |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((move |$sc2_param : $ty2| $body2)((move |$($c_params),*| $body)($($expr),*)), $($other)*)
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((|$c2_param : $ty2| $body2)((|$($c_params: $ty),*| $body)($($expr),*)), $($other)*)
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((|$sc2_param : $ty2| $body2)(move |$($c_params: $ty),*| $body)($($expr),*), $($other)*)
};
((|$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((move |$sc2_param : $ty2| $body2)(|$($c_params: $ty),*| $body)($($expr),*), $($other)*)
};
((move |$($c_params: ident: $ty: ty),*| $body: expr)($($expr: expr),*), move |$c2_param: ident: $ty2: ty| $body2: expr, $($other: tt)*) => {
chain!((move |$sc2_param : $ty2| $body2)(move |$($c_params: $ty),*| $body)($($expr),*), $($other)*)
};
}
pub struct Chainable<R> {
val: R
}
impl<R> Chainable<R> {
pub fn chain<CR, F>(self, func: F) -> Chainable<CR> where F: FnOnce(R) -> CR {
Chainable {
val: func(self.val)
}
}
pub fn end(self) -> R {
self.val
}
}
impl<R> std::ops::Deref for Chainable<R> {
type Target = R;
fn deref(&self) -> &R {
&self.val
}
}
pub fn chain<R>(result: R) -> Chainable<R> {
Chainable { val: result }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn func_chain() {
fn some_func(a: i64) -> i64 {
a
}
fn add_one(b: i64) -> i64 {
b + 1
}
assert_eq!(1.5f64, *chain(some_func(1)).chain(add_one).chain(add_one).chain(|c| (c as f64) / 2f64));
assert_eq!(10, *chain(some_func(1)).chain(add_one).chain(|c| c * 2).chain(|c| c * 2).chain(add_one).chain(add_one));
}
#[test]
fn func_chain_moved() {
fn some_func(a: i64) -> i64 {
a
}
fn add_one(b: i64) -> i64 {
b + 1
}
assert_eq!(10, *chain(some_func(1)).chain(add_one).chain(move |c| c * 2).chain(move |c| c * 2).chain(add_one).chain(add_one));
assert_eq!(10, *chain(some_func(1)).chain(add_one).chain(|c| c * 2).chain(move |c| c * 2).chain(add_one).chain(add_one));
assert_eq!(10, *chain(some_func(1)).chain(add_one).chain(move |c| c * 2).chain(|c| c * 2).chain(add_one).chain(add_one));
assert_eq!(14, *chain::<i64>((|a, b| a + b)(1i64, 1)).chain(add_one).chain(move |c| c * 2).chain(|c| c * 2).chain(add_one).chain(add_one));
assert_eq!(6.5f64, *chain::<i64>((|a, b| a + b)(1i64, 1)).chain(add_one).chain(move |c| c * 2).chain(|c| c * 2).chain(add_one).chain(|d| {d as f64 / 2f64}));
}
#[test]
fn macro_chain() {
fn some_func(a: i64) -> i64 {
a
}
fn another_func(b: i64) -> i64 {
b + 1
}
assert_eq!(3, chain!(some_func(1), another_func, another_func));
}
#[test]
fn macro_chain_closure() {
fn some_func(a: i64) -> i64 {
a
}
fn some_add(a: i64, b: i64, c: i64) -> i64 {
a + b + c
}
fn pass_through(v: f64) -> f64 {
v
}
assert_eq!(2, chain!(some_func(1), |a| {return a + 1}, |b| {return b * 1}, some_func, some_func));
assert_eq!(6f64, chain!(some_add(1, 2, 3), |result: i64| {(result as f64).powi(2)}, |sqr: f64| {sqr.powf(0.5)}, pass_through, pass_through));
}
#[test]
fn macro_chain_moved_closure() {
fn some_func(a: i64) -> i64 {
a
}
let value = 2;
let one = 1;
assert_eq!(3, chain!(some_func(1), move |a| {return a + value}, move |b| {return b * one}, some_func, some_func));
assert_eq!(3, chain!(some_func(1), |a| {return a + value}, move |b| {return b * one}, some_func, some_func));
assert_eq!(3, chain!(some_func(1), move |a| {return a + value}, |b| {return b * one}, some_func, some_func));
}
}