#[macro_export]
macro_rules! hlist {
() => { $crate::hlist::Nihil };
(...$rest:expr) => { $rest };
($a:expr) => { $crate::hlist![$a,] };
($a:expr, $($tok:tt)*) => {
$crate::hlist::Coniunctio {
head: $a,
tail: $crate::hlist![$($tok)*],
}
};
}
#[macro_export]
macro_rules! coniunctio_pat {
() => { $crate::hlist::Nihil };
(...) => { _ };
(...$rest:pat) => { $rest };
(_) => { $crate::coniunctio_pat![_,] };
($a:pat) => { $crate::coniunctio_pat![$a,] };
(_, $($tok:tt)*) => {
$crate::hlist::Coniunctio {
tail: $crate::coniunctio_pat![$($tok)*],
..
}
};
($a:pat, $($tok:tt)*) => {
$crate::hlist::Coniunctio {
head: $a,
tail: $crate::coniunctio_pat![$($tok)*],
}
};
}
#[macro_export]
macro_rules! HList {
() => { $crate::hlist::Nihil };
(...$Rest:ty) => { $Rest };
($A:ty) => { $crate::HList![$A,] };
($A:ty, $($tok:tt)*) => {
$crate::hlist::Coniunctio<$A, $crate::HList![$($tok)*]>
};
}
#[macro_export]
macro_rules! Disiunctio {
() => { $crate::disiunctio::Absurdum };
(...$Rest:ty) => { $Rest };
($A:ty) => { $crate::Disiunctio![$A,] };
($A:ty, $($tok:tt)*) => {
$crate::disiunctio::Disiunctio<$A, $crate::Disiunctio![$($tok)*]>
};
}
#[macro_export]
macro_rules! field {
(($($repeated: ty),*), $value: expr) => {
$crate::field!( ($($repeated),*), $value, concat!( $(stringify!($repeated)),* ) )
};
(($($repeated: ty,)*), $value: expr) => {
$crate::field!( ($($repeated),*), $value )
};
($name_type: ty, $value: expr) => {
$crate::field!( $name_type, $value, stringify!($name_type) )
};
($name_type: ty, $value: expr, $name: expr) => {
$crate::labelled::field_with_name::<$name_type,_>($name, $value)
}
}
#[macro_export]
macro_rules! functio_poly {
([$($tparams: tt),*] |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block , $($rest: tt)*)
=> { $crate::functio_poly!(
p~ [$($tparams, )*] |$arg: $arg_typ| -> $ret_typ $body, ~p f~ ~f $($rest)*
)};
([$($tparams: tt, )*] |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block , $($rest: tt)*)
=> { $crate::functio_poly!(
p~ [$($tparams, )*] |$arg: $arg_typ| -> $ret_typ $body, ~p f~ ~f $($rest)*
)};
(|$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block, $($rest: tt)*)
=> { $crate::functio_poly!(
p~ ~p f~ |$arg: $arg_typ| -> $ret_typ $body, ~f $($rest)*
)};
(p~ $([$($pars: tt, )*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty $p_body: block , )* ~p f~ $(|$f_args: ident : $f_arg_typ: ty| -> $f_ret_typ: ty $f_body: block , )* ~f [$($tparams: tt),*] |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block , $($rest: tt)*)
=> { $crate::functio_poly!(
p~ [$($tparams, )*] |$arg: $arg_typ| -> $ret_typ $body, $( [$($pars, )*] |$p_args: $p_arg_typ| -> $p_ret_typ $p_body, )* ~p f~ $(|$f_args: $f_arg_typ| -> $f_ret_typ $f_body, )* ~f $($rest)*
)};
(p~ $([$($pars: tt, )*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty { $p_body: block }, )* ~p f~ $(|$f_args: ident : $f_arg_typ: ty| -> $f_ret_typ: ty $f_body: block, )* ~f [$($tparams: tt, )*] |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block, $($rest: tt)*)
=> { $crate::functio_poly!(
p~ [$($tparams, )*] |$arg: $arg_typ| -> $ret_typ $body, $( [$($pars, )*] |$p_args: $p_arg_typ| -> $p_ret_typ $p_body, )* ~p f~ $(|$f_args: $f_arg_typ| -> $f_ret_typ $f_body, )* ~f $($rest)*
)};
(p~ $([$($pars: tt, )*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty $p_body: block, )* ~p f~ $(|$f_args: ident : $f_arg_typ: ty| -> $f_ret_typ: ty $f_body: block, )* ~f |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block, $($rest: tt)*)
=> { $crate::functio_poly!(
p~ $( [$($pars, )*] |$p_args: $p_arg_typ| -> $p_ret_typ $p_body, )* ~p f~ |$arg: $arg_typ| -> $ret_typ $body, $(|$f_args: $f_arg_typ| -> $f_ret_typ $f_body, )* ~f $($rest)*
)};
(p~ $([$($pars: tt, )*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty $p_body: block, )* ~p f~ $(|$f_args: ident : $f_arg_typ: ty| -> $f_ret_typ: ty $f_body: block, )* ~f [$($tparams: tt),*] |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block)
=> { $crate::functio_poly!(
p~ [$($tparams, )*] |$arg: $arg_typ| -> $ret_typ $body, $( [$($pars, )*] |$p_args: $p_arg_typ| -> $p_ret_typ $p_body, )* ~p f~ $(|$f_args: $f_arg_typ| -> $f_ret_typ $f_body, )* ~f
)};
(p~ $([$($pars: tt, )*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty $p_body: block, )* ~p f~ $(|$f_args: ident : $f_arg_typ: ty| -> $f_ret_typ: ty $f_body: block, )* ~f [$($tparams: tt, )*] |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block)
=> { $crate::functio_poly!(
p~ [$($tparams, )*] |$arg: $arg_typ| -> $ret_typ $body, $( [$($pars, )*] |$p_args: $p_arg_typ| -> $p_ret_typ $p_body, )* ~p f~ $(|$f_args: $f_arg_typ| -> $f_ret_typ $f_body, )* ~f
)};
(p~ $([$($pars: tt)*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty $p_body: block, )* ~p f~ $(|$f_args: ident : $f_arg_typ: ty| -> $f_ret_typ: ty $f_body: block, )* ~f |$arg: ident : $arg_typ: ty| -> $ret_typ: ty $body: block)
=> { $crate::functio_poly!(
p~ $( [$($pars, )*] |$p_args: $p_arg_typ| -> $p_ret_typ $p_body, )* ~p f~ |$arg: $arg_typ| -> $ret_typ $body, $(|$f_args: $f_arg_typ| -> $f_ret_typ $f_body, )* ~f
)};
(p~ $([$($pars: tt, )*] |$p_args: ident : $p_arg_typ: ty| -> $p_ret_typ: ty $p_body: block, )* ~p f~ $(|$args: ident : $arg_typ: ty| -> $ret_typ: ty $body: block, )* ~f) => {{
struct F;
$(
impl<$($pars,)*> $crate::traits::Func<$p_arg_typ> for F {
type Output = $p_ret_typ;
#[inline(always)]
fn call($p_args: $p_arg_typ) -> Self::Output { $p_body }
}
)*
$(
impl $crate::traits::Func<$arg_typ> for F {
type Output = $ret_typ;
#[inline(always)]
fn call($args: $arg_typ) -> Self::Output { $body }
}
)*
$crate::traits::Poly(F)
}}
}
#[macro_export]
macro_rules! mdo {
($e:expr) => { $e };
(let $x:ident = bind $e:expr; $($rest:tt)+) => {
$e.and_then(move |$x| $crate::mdo!($($rest)+))
};
(let $x:ident = pure $e:expr; $($rest:tt)+) => {{
let $x = $e;
$crate::mdo!($($rest)+)
}};
(let $p:pat = $e:expr; $($rest:tt)+) => {{
let $p = $e;
$crate::mdo!($($rest)+)
}};
($s:stmt; $($rest:tt)+) => {{
$s;
$crate::mdo!($($rest)+)
}};
}
#[macro_export]
macro_rules! compose {
($f:expr) => { $f };
($f:expr, $($rest:expr),+) => {
|x| $f($crate::compose!($($rest),+)(x))
};
}
#[macro_export]
macro_rules! pipe {
($x:expr) => { $x };
($x:expr, $f:expr) => { $f($x) };
($x:expr, $f:expr, $($rest:expr),+) => {
$crate::pipe!($f($x), $($rest),+)
};
}
#[macro_export]
macro_rules! curry2 {
($f:expr) => {
move |x| move |y| $f(x, y)
};
}
#[macro_export]
macro_rules! curry3 {
($f:expr) => {
move |x| move |y| move |z| $f(x, y, z)
};
}
#[macro_export]
macro_rules! flip {
($f:expr) => {
|y, x| $f(x, y)
};
}
#[macro_export]
macro_rules! constant {
($x:expr) => {
|_| $x
};
}
#[macro_export]
macro_rules! cold_panic {
($($arg:tt)*) => {{
#[cold]
#[inline(never)]
fn cold_panic_impl(args: core::fmt::Arguments) -> ! {
panic!("{}", args)
}
cold_panic_impl(format_args!($($arg)*))
}};
}
#[macro_export]
macro_rules! unlikely_panic {
($cond:expr, $($arg:tt)*) => {
if $cond {
$crate::cold_panic!($($arg)*);
}
};
}
#[macro_export]
#[cfg(feature = "async")]
macro_rules! mdo_async {
($e:expr) => { $e };
(let $x:ident = bind $e:expr; $($rest:tt)+) => {
$e.and_then(move |$x| $crate::mdo_async!($($rest)+))
};
(let $x:ident = await $e:expr; $($rest:tt)+) => {{
let $x = $e.await;
$crate::mdo_async!($($rest)+)
}};
(let $x:ident = pure $e:expr; $($rest:tt)+) => {{
let $x = $e;
$crate::mdo_async!($($rest)+)
}};
(let $p:pat = $e:expr; $($rest:tt)+) => {{
let $p = $e;
$crate::mdo_async!($($rest)+)
}};
($s:stmt; $($rest:tt)+) => {{
$s;
$crate::mdo_async!($($rest)+)
}};
}
#[macro_export]
#[cfg(feature = "async")]
macro_rules! compose_async {
($f:expr) => {
|x| $f(x)
};
($f:expr, $($rest:expr),+) => {
|x| async move {
let intermediate = $crate::compose_async!($($rest),+)(x).await;
$f(intermediate).await
}
};
}
#[macro_export]
#[cfg(feature = "async")]
macro_rules! pipe_async {
($x:expr) => { async move { $x } };
($x:expr, $f:expr) => { $f($x) };
($x:expr, $f:expr, $($rest:expr),+) => {
async move {
let intermediate = $f($x).await;
$crate::pipe_async!(intermediate, $($rest),+).await
}
};
}
#[macro_export]
#[cfg(feature = "async")]
macro_rules! chain_async {
($f:expr) => {
|x| $f(x)
};
($f:expr, $($rest:expr),+) => {
|x| async move {
let intermediate = $f(x).await;
$crate::chain_async!($($rest),+)(intermediate).await
}
};
}
#[cfg(test)]
mod tests {
#[test]
#[allow(clippy::diverging_sub_expression)]
fn trailing_commas() {
use crate::test_structs::unit_copy::{A, B};
let coniunctio_pat![]: HList![] = hlist![];
let coniunctio_pat![A]: HList![A] = hlist![A];
let coniunctio_pat![A,]: HList![A,] = hlist![A,];
let coniunctio_pat![A, B]: HList![A, B] = hlist![A, B];
let coniunctio_pat![A, B,]: HList![A, B,] = hlist![A, B,];
let falsum = || false;
if falsum() {
let _: Disiunctio![] = panic!();
}
if falsum() {
let _: Disiunctio![A] = panic!();
}
if falsum() {
let _: Disiunctio![A,] = panic!();
}
if falsum() {
let _: Disiunctio![A, B] = panic!();
}
if falsum() {
let _: Disiunctio![A, B,] = panic!();
}
}
#[test]
fn ellipsis_tail() {
use crate::disiunctio::Disiunctio;
use crate::test_structs::unit_copy::{A, B, C};
let coniunctio_pat![...coniunctio_pat![C]]: HList![...HList![C]] = { hlist![...hlist![C]] };
let coniunctio_pat![A, ...coniunctio_pat![C]]: HList![A, ...HList![C]] =
{ hlist![A, ...hlist![C]] };
let coniunctio_pat![A, B, ...coniunctio_pat![C]]: HList![A, B, ...HList![C]] =
{ hlist![A, B, ...hlist![C]] };
let coniunctio_pat![A, B, C] = hlist![A, ...hlist![B, C]];
let coniunctio_pat![A, ...coniunctio_pat![B, C]] = hlist![A, B, C];
let choice: Disiunctio![A, B, C] = Disiunctio::inject(A);
let _: Disiunctio![...Disiunctio![A, B, C]] = choice;
let _: Disiunctio![A, ...Disiunctio![B, C]] = choice;
let _: Disiunctio![A, B, ...Disiunctio![C]] = choice;
}
#[test]
fn ellipsis_ignore() {
use crate::test_structs::unit_copy::{A, B, C, D, E};
let coniunctio_pat![...] = hlist![A, B, C, D, E];
let coniunctio_pat![A, ...] = hlist![A, B, C, D, E];
let coniunctio_pat![A, B, ...] = hlist![A, B, C, D, E];
}
#[test]
fn functio_poly_macro_test() {
let h = hlist![9000, "joe", 41f32, "schmoe", 50];
let h2 = h.map(functio_poly!(
|x: i32| -> bool { x > 100 },
|_x: f32| -> &'static str { "dummy" },
['a] |x: &'a str| -> usize { x.len() }
));
assert_eq!(h2, hlist![true, 3, "dummy", 6, false]);
}
#[test]
fn functio_poly_macro_disiunctio_test() {
type I32F32StrBool<'a> = Disiunctio!(i32, f32, &'a str);
let co1 = I32F32StrBool::inject("lollerskates");
let folded = co1.fold(functio_poly!(
['a] |_x: &'a str| -> i8 { 1 },
|_x: i32| -> i8 { 2 },
|_f: f32| -> i8 { 3 },
));
assert_eq!(folded, 1);
}
#[test]
fn functio_poly_macro_trailing_commas_test() {
let h = hlist![9000, "joe", 41f32, "schmoe", 50];
let h2 = h.map(functio_poly!(
|x: i32| -> bool { x > 100 },
|_x: f32| -> &'static str { "dummy" },
['a,] |x: &'a str| -> usize { x.len() },
));
assert_eq!(h2, hlist![true, 3, "dummy", 6, false]);
}
#[test]
fn functio_poly_macro_multiline_bodies_test() {
let h = hlist![9000, 1, -1];
let h2 = h.map(functio_poly!(|x: i32| -> bool {
let a = if x > 100 { 1 } else { -1 };
a > 0
},));
assert_eq!(h2, hlist![true, false, false]);
}
#[test]
#[deny(clippy::unneeded_field_pattern)]
fn unneeded_field_pattern() {
let coniunctio_pat![_, _] = hlist![1, 2];
let coniunctio_pat![foo, _, baz] = hlist!["foo", "bar", "baz"];
assert_eq!(foo, "foo");
assert_eq!(baz, "baz");
}
#[test]
fn test_mdo_option_success() {
let result = mdo! {
let x = bind Some(10);
let y = bind Some(5);
Some(x + y)
};
assert_eq!(result, Some(15));
}
#[test]
fn test_mdo_option_short_circuit() {
let result = mdo! {
let x = bind Some(10);
let y = bind None::<i32>;
Some(x + y)
};
assert_eq!(result, None);
}
#[test]
fn test_mdo_option_with_pure() {
let result = mdo! {
let x = bind Some(10);
let doubled = pure x * 2;
let y = bind Some(5);
Some(doubled + y)
};
assert_eq!(result, Some(25));
}
#[test]
fn test_mdo_result_success() {
fn parse(s: &str) -> Result<i32, &'static str> {
s.parse().map_err(|_| "parse error")
}
let result: Result<i32, &str> = mdo! {
let x = bind parse("10");
let y = bind parse("5");
Ok(x + y)
};
assert_eq!(result, Ok(15));
}
#[test]
fn test_mdo_result_failure() {
fn parse(s: &str) -> Result<i32, &'static str> {
s.parse().map_err(|_| "parse error")
}
let result: Result<i32, &str> = mdo! {
let x = bind parse("10");
let y = bind parse("not_a_number");
Ok(x + y)
};
assert_eq!(result, Err("parse error"));
}
#[test]
fn test_compose_single() {
let f = |x: i32| x + 1;
let composed = compose!(f);
assert_eq!(composed(10), 11);
}
#[test]
fn test_compose_multiple() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let h = |x: i32| x - 3;
let composed = compose!(f, g, h);
assert_eq!(composed(10), 15);
}
#[test]
fn test_pipe_single() {
let result = pipe!(10);
assert_eq!(result, 10);
}
#[test]
fn test_pipe_one_function() {
let f = |x: i32| x + 1;
let result = pipe!(10, f);
assert_eq!(result, 11);
}
#[test]
fn test_pipe_multiple() {
let f = |x: i32| x + 1;
let g = |x: i32| x * 2;
let h = |x: i32| x - 3;
let result = pipe!(10, f, g, h);
assert_eq!(result, 19);
}
#[test]
fn test_curry2() {
let add = |a: i32, b: i32| a + b;
let curried = curry2!(add);
let add5 = curried(5);
assert_eq!(add5(10), 15);
assert_eq!(add5(20), 25);
}
#[test]
fn test_curry3() {
let add3 = |a: i32, b: i32, c: i32| a + b + c;
let curried = curry3!(add3);
let partial1 = curried(1);
let partial2 = partial1(2);
assert_eq!(partial2(3), 6);
}
#[test]
fn test_flip() {
let sub = |a: i32, b: i32| a - b;
let flipped = flip!(sub);
assert_eq!(sub(10, 3), 7);
assert_eq!(flipped(10, 3), -7); }
#[test]
fn test_constant() {
let always_42 = constant!(42);
assert_eq!(always_42(100), 42);
assert_eq!(always_42(0), 42);
}
}