extern crate clean_macro_docs;
use clean_macro_docs::clean_docs;
#[clean_docs]
#[macro_export]
macro_rules! c {
(@preprocess[0] {: $($ts:tt)*} {$($procd_ts:tt)*}) =>
{ c!(@preprocess[1] {$($ts)*} {$($procd_ts)* =>}) };
(@preprocess[0] {for $($ts:tt)*} {$($procd_ts:tt)*}) =>
{ c!(@preprocess[1] {for $($ts)*} {$($procd_ts)*}) };
(@preprocess[0] {$t:tt $($ts:tt)*} {$($procd_ts:tt)*}) =>
{ c!(@preprocess[0] {$($ts)*} {$($procd_ts)* $t}) };
(@preprocess[0] {} {$($procd_ts:tt)*}) =>
{ compile_error!("Comprehension must contain at least one `for ... in ...` expression") };
(@preprocess[1] {$($ts:tt)*} {, $($procd_ts:tt)*}) =>
{ compile_error!("Missing loop body") };
(@preprocess[1] {for $($ts:tt)*} {$($procd_ts:tt)*}) =>
{ c!(@preprocess[1] {$($ts)*} {$($procd_ts)* , for}) };
(@preprocess[1] {if $($ts:tt)*} {$($procd_ts:tt)*}) =>
{ c!(@preprocess[1] {$($ts)*} {$($procd_ts)* , if}) };
(@preprocess[1] {$t:tt $($ts:tt)*} {$($procd_ts:tt)*}) =>
{ c!(@preprocess[1] {$($ts)*} {$($procd_ts)* $t}) };
(@preprocess[1] {} {$($procd_ts:tt)*}) =>
{ c!(@construct[0] $($procd_ts)*) };
(@construct[0] $k:expr => $v:expr, for $($rest:tt)*) => {{
let mut m = std::collections::HashMap::new();
c![@construct[1] {m.insert($k, $v);}, for $($rest)*];
m
}};
(@construct[0] $e:expr, for $($rest:tt)*) => {{
let mut v = Vec::new();
c![@construct[1] v.push($e), for $($rest)*];
v
}};
(@construct[0] $s:stmt;, for $($rest:tt)*) => {{
c![@construct[1] $s, for $($rest)*];
}};
(@construct[1] $s:stmt, for $el:ident in $iter:expr $(, $($rest:tt)*)?) => {{
for $el in $iter {
c![@construct[1] $s $(, $($rest)*)?]
}
}};
(@construct[1] $s:stmt, for $p:pat in $iter:expr $(, $($rest:tt)*)?) => {{
for $p in $iter {
c![@construct[1] $s $(, $($rest)*)?]
}
}};
(@construct[1] $s:stmt, for $($rest:tt)*) => {{
compile_error!("Invalid for-loop")
}};
(@construct[1] $s:stmt, if $cond:expr $(, $($rest:tt)*)?) => {{
if $cond {
c![@construct[1] $s $(, $($rest)*)?]
}
}};
(@construct[1] $s:stmt, if $($rest:tt)*) => {{
compile_error!("Invalid if-expression")
}};
(@construct[1] $s:stmt) => {{
$s
}};
($($comp:tt)*) => {{
c!(@preprocess[0] {$($comp)*} {})
}};
}
#[cfg(test)]
mod tests {
#[test]
fn simple_vec() {
let v = c![x * x for x in 1..=10];
assert_eq!(v, vec![1, 4, 9, 16, 25, 36, 49, 64, 81, 100]);
}
#[test]
fn simple_cond_vec() {
let v = c![x * x for x in 1..=10 if x % 2 == 0];
assert_eq!(v, vec![4, 16, 36, 64, 100]);
}
#[test]
fn for_for_vec() {
let v = c![(x, y) for x in 1..=3 for y in 'a'..='c'];
assert_eq!(
v,
vec![
(1, 'a'),
(1, 'b'),
(1, 'c'),
(2, 'a'),
(2, 'b'),
(2, 'c'),
(3, 'a'),
(3, 'b'),
(3, 'c')
]
);
}
#[test]
fn for_for_if_vec() {
let v = c![(x, y) for x in 1..=3 for y in 'a'..='c' if x % 2 != 0];
assert_eq!(
v,
vec![(1, 'a'), (1, 'b'), (1, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]
);
}
#[test]
fn for_if_for_vec() {
let v = c![(x, y) for x in 1..=3 if x % 2 != 0 for y in 'a'..='c'];
assert_eq!(
v,
vec![(1, 'a'), (1, 'b'), (1, 'c'), (3, 'a'), (3, 'b'), (3, 'c')]
);
}
#[test]
fn simple_map() {
let m = c! {x: x * x for x in 1..=10};
assert_eq!(
m,
[
(1, 1),
(2, 4),
(3, 9),
(4, 16),
(5, 25),
(6, 36),
(7, 49),
(8, 64),
(9, 81),
(10, 100),
]
.iter()
.cloned()
.collect()
);
}
#[test]
fn simple_cond_map() {
let m = c! {x: x * x for x in 1..=10 if x % 2 == 0};
assert_eq!(
m,
[(2, 4), (4, 16), (6, 36), (8, 64), (10, 100)]
.iter()
.cloned()
.collect()
);
}
#[test]
fn for_for_map() {
let m = c! {format!("{}|{}", x, y): (x, y) for x in 1..=3 for y in 'a'..='c'};
assert_eq!(
m,
[
("1|a".to_string(), (1, 'a')),
("1|b".to_string(), (1, 'b')),
("1|c".to_string(), (1, 'c')),
("2|a".to_string(), (2, 'a')),
("2|b".to_string(), (2, 'b')),
("2|c".to_string(), (2, 'c')),
("3|a".to_string(), (3, 'a')),
("3|b".to_string(), (3, 'b')),
("3|c".to_string(), (3, 'c')),
]
.iter()
.cloned()
.collect()
);
}
#[test]
fn for_for_if_map() {
let m = c! {format!("{}|{}", x, y): (x, y) for x in 1..=3 for y in 'a'..='c' if x % 2 != 0};
assert_eq!(
m,
[
("1|a".to_string(), (1, 'a')),
("1|b".to_string(), (1, 'b')),
("1|c".to_string(), (1, 'c')),
("3|a".to_string(), (3, 'a')),
("3|b".to_string(), (3, 'b')),
("3|c".to_string(), (3, 'c')),
]
.iter()
.cloned()
.collect()
);
}
#[test]
fn for_if_for_map() {
let m = c! {format!("{}|{}", x, y): (x, y) for x in 1..=3 if x % 2 != 0 for y in 'a'..='c'};
assert_eq!(
m,
[
("1|a".to_string(), (1, 'a')),
("1|b".to_string(), (1, 'b')),
("1|c".to_string(), (1, 'c')),
("3|a".to_string(), (3, 'a')),
("3|b".to_string(), (3, 'b')),
("3|c".to_string(), (3, 'c')),
]
.iter()
.cloned()
.collect()
);
}
#[test]
fn simple_stmt() {
let mut n = 0;
c!(n += x * x; for x in 1..=10);
assert_eq!(n, 385);
}
#[test]
fn simple_cond_stmt() {
let mut n = 0;
c!(n += x * x; for x in 1..=10 if x % 2 == 0);
assert_eq!(n, 220);
}
#[test]
fn for_for_stmt() {
let mut s = String::new();
c!(s += &format!("[{}|{}]", x, y); for x in 1..=3 for y in 'a'..='c');
assert_eq!(s, "[1|a][1|b][1|c][2|a][2|b][2|c][3|a][3|b][3|c]");
}
#[test]
fn for_for_if_stmt() {
let mut s = String::new();
c!(s += &format!("[{}|{}]", x, y); for x in 1..=3 for y in 'a'..='c' if x % 2 != 0);
assert_eq!(s, "[1|a][1|b][1|c][3|a][3|b][3|c]");
}
#[test]
fn for_if_for_stmt() {
let mut s = String::new();
c!(s += &format!("[{}|{}]", x, y); for x in 1..=3 if x % 2 != 0 for y in 'a'..='c');
assert_eq!(s, "[1|a][1|b][1|c][3|a][3|b][3|c]");
}
}