#![cfg_attr(not(test), no_std)]
#[macro_export]
macro_rules! using {
($target:expr => { $( $t:tt )* }) => {
{
#[allow(unused_mut)]
let mut target = $target;
$crate::using_impl!(target root empty { $($t)* })
}
};
($id:ident @ $target:expr => { $( $t:tt )* }) => {
{
#[allow(unused_mut)]
let mut $id = $target;
$crate::using_impl!($id root empty { $($t)* })
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! using_impl {
($target:ident $scope:ident maybe_trailing_exp ($id:ident) { }) => {
$id
};
($target:ident $scope:ident maybe_trailing_exp ($id:ident) { ; $($rest:tt)* }) => {
$crate::using_impl!($target $scope empty { $($rest)* })
};
($target:ident $scope:ident maybe_trailing_exp ($id:ident) { $($rest:tt)* }) => {
$crate::using_impl!($target $scope empty { $($rest)* })
};
($target:ident root empty { }) => {
$target
};
($target:ident block empty { }) => {
#[allow(unreachable_code)]
()
};
($target:ident $scope:ident empty { ; $($rest:tt)* }) => {
{
;
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident empty { . $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_exp ($target) { . $($rest)* })
};
($target:ident $scope:ident in_exp ($exp:expr) { . $name:ident $( ::<$($ty:ty),* $(,)?> )? ( $($args:expr),* $(,)? ) $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_exp ($exp.$name$(::<$($ty),*>)*($($args),*)) { $($rest)* })
};
($target:ident $scope:ident in_exp ($exp:expr) { . $name:ident $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_exp ($exp.$name) { $($rest)* })
};
($target:ident $scope:ident in_exp ($exp:expr) { }) => {
$exp
};
($target:ident $scope:ident in_exp ($exp:expr) { ; $($rest:tt)* }) => {
{
$exp;
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident in_exp ($exp:expr) { . $name:ident = $value:expr; $($rest:tt)* }) => {
{
$exp.$name = $value;
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident empty { { $($block:tt)* } }) => {
$crate::using_impl!($target block empty { $($block)* })
};
($target:ident $scope:ident empty { { $($block:tt)* } $($rest:tt)* }) => {
{
$crate::using_impl!($target block empty { $($block)* });
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident empty { let $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_let () { $($rest)* })
};
($target:ident $scope:ident in_let
($($pattern:tt)*)
{ = $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_let_exp ($($pattern)*) (_) () { $($rest)* })
};
($target:ident $scope:ident in_let
($($pattern:tt)*)
{ : $ty:ty = $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_let_exp ($($pattern)*) ($ty) () { $($rest)* })
};
($target:ident $scope:ident in_let
($($pattern:tt)*)
{ $t:tt $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_let ($($pattern)* $t) { $($rest)* })
};
($target:ident $scope:ident in_let_exp
($pattern:pat)
($ty:ty)
($($exp:tt)*)
{ ; $($rest:tt)* }
) => {
{
let $pattern: $ty = $crate::using_impl!($target block empty { $($exp)* });
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident in_let_exp
($pattern:pat)
($ty:ty)
($($exp:tt)*)
{ $t:tt $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_let_exp ($pattern) ($ty) ($($exp)* $t) { $($rest)* })
};
($target:ident $scope:ident empty { if $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_if () () () { $($rest)* })
};
($target:ident $scope:ident in_if
($($if_curr:tt)*)
()
()
{ { $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_if_next
()
(($($if_curr)*) { $($body)* })
()
{ $($rest)* }
)
};
($target:ident $scope:ident in_if
($($if_curr:tt)*)
($($if_first:tt)*)
($($if_rest:tt)*)
{ { $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_if_next
()
($($if_first)*)
($($if_rest)* (($($if_curr)*) { $($body)* }))
{ $($rest)* }
)
};
($target:ident $scope:ident in_if
($($if_curr:tt)*)
($($if_first:tt)*)
($($if_rest:tt)*)
{ $t:tt $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_if
($($if_curr)* $t)
($($if_first)*)
($($if_rest)*)
{ $($rest)* }
)
};
($target:ident $scope:ident in_if_next
()
($($if_first:tt)*)
($($if_rest:tt)*)
{ else if $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_if
()
($($if_first)*)
($($if_rest)*)
{ $($rest)* }
)
};
($target:ident $scope:ident in_if_next
()
(($($if_first_cond:tt)*) { $($if_first_body:tt)* })
($( (($($if_rest_cond:tt)*) { $($if_rest_body:tt)* }) )*)
{ else { $($body:tt)* } $($rest:tt)* }
) => {
{
let _tmp = if $($if_first_cond)* {
$crate::using_impl!($target block empty { $($if_first_body)* })
} $( else if $($if_rest_cond)* {
$crate::using_impl!($target block empty { $($if_rest_body)* })
} )* else {
$crate::using_impl!($target block empty { $($body)* })
};
$crate::using_impl!($target $scope maybe_trailing_exp (_tmp) { $($rest)* })
}
};
($target:ident $scope:ident in_if_next
()
(($($if_first_cond:tt)*) { $($if_first_body:tt)* })
($( (($($if_rest_cond:tt)*) { $($if_rest_body:tt)* }) )*)
{ $($rest:tt)* }
) => {
{
if $($if_first_cond)* {
$crate::using_impl!($target block empty { $($if_first_body)* })
} $( else if $($if_rest_cond)* {
$crate::using_impl!($target block empty { $($if_rest_body)* })
} )*
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident empty { match $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_match () { $($rest)* })
};
($target:ident $scope:ident in_match
($($match_cond:tt)*)
{ { $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body ($($match_cond)*) () { { $($body)* } $($rest)* })
};
($target:ident $scope:ident in_match
($($match_cond:tt)*)
{ $t:tt $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match ($($match_cond)* $t) { $($rest)* })
};
($target:ident $scope:ident in_match_body
($($match_cond:tt)*)
($($match_cases:tt)*)
{ { $pattern:pat $( if $guard:expr )? => . $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body_in_exp
($($match_cond)*)
($($match_cases)*)
(($pattern) $($guard)*)
(.)
{ { $($body)* } $($rest)* }
)
};
($target:ident $scope:ident in_match_body_in_exp
($($match_cond:tt)*)
($($match_cases:tt)*)
(($match_pattern:pat) $($match_guard:expr)?)
($($match_exp:tt)*)
{ { , $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body
($($match_cond)*)
($($match_cases)* ($match_pattern $( if $match_guard )* => { $($match_exp)* }))
{ { $($body)* } $($rest)* }
)
};
($target:ident $scope:ident in_match_body_in_exp
($($match_cond:tt)*)
($($match_cases:tt)*)
(($match_pattern:pat) $($match_guard:expr)?)
($($match_exp:tt)*)
{ { } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body
($($match_cond)*)
($($match_cases)* ($match_pattern $( if $match_guard )* => { $($match_exp)* }))
{ { } $($rest)* }
)
};
($target:ident $scope:ident in_match_body_in_exp
($($match_cond:tt)*)
($($match_cases:tt)*)
(($match_pattern:pat) $($match_guard:expr)?)
($($match_exp:tt)*)
{ { $t:tt $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body_in_exp
($($match_cond)*)
($($match_cases)*)
(($match_pattern) $($match_guard)*)
($($match_exp)* $t)
{ { $($body)* } $($rest)* }
)
};
($target:ident $scope:ident in_match_body
($($match_cond:tt)*)
($($match_cases:tt)*)
{ { $pattern:pat $( if $guard:expr )? => { $($exp:tt)* }, $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body
($($match_cond)*)
($($match_cases)* ($pattern $( if $guard )* => { $($exp)* }))
{ { $($body)* } $($rest)* }
)
};
($target:ident $scope:ident in_match_body
($($match_cond:tt)*)
($($match_cases:tt)*)
{ { $pattern:pat $( if $guard:expr )? => { $($exp:tt)* } $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body
($($match_cond)*)
($($match_cases)* ($pattern $( if $guard )* => { $($exp)* }))
{ { $($body)* } $($rest)* }
)
};
($target:ident $scope:ident in_match_body
($($match_cond:tt)*)
($($match_cases:tt)*)
{ { $pattern:pat $( if $guard:expr )? => $exp:expr, $($body:tt)* } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body
($($match_cond)*)
($($match_cases)* ($pattern $( if $guard )* => { $exp }))
{ { $($body)* } $($rest)* }
)
};
($target:ident $scope:ident in_match_body
($($match_cond:tt)*)
($($match_cases:tt)*)
{ { $pattern:pat $( if $guard:expr )? => $exp:expr } $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_match_body
($($match_cond)*)
($($match_cases)* ($pattern $( if $guard )* => { $exp }))
{ { } $($rest)* }
)
};
($target:ident $scope:ident in_match_body
($($match_cond:tt)*)
($( ($pattern:pat $( if $guard:expr )? => { $($exp:tt)* }) )*)
{ { } $($rest:tt)* }
) => {
{
let _tmp = match $($match_cond)* {
$( $pattern $( if $guard )* => { $crate::using_impl!($target block empty { $($exp)* }) }, )*
};
$crate::using_impl!($target $scope maybe_trailing_exp (_tmp) { $($rest)* })
}
};
($target:ident $scope:ident empty { loop { $($body:tt)* } $($rest:tt)* }) => {
{
let _tmp = loop {
$crate::using_impl!($target block empty { $($body)* })
};
$crate::using_impl!($target $scope maybe_trailing_exp (_tmp) { $($rest)* })
}
};
($target:ident $scope:ident empty { while $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_while () { $($rest)* })
};
($target:ident $scope:ident in_while
($($while_cond:tt)*)
{ { $($body:tt)* } $($rest:tt)* }
) => {
{
while $($while_cond)* {
$crate::using_impl!($target block empty { $($body)* })
}
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident in_while
($($while_cond:tt)*)
{ $t:tt $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_while ($($while_cond)* $t) { $($rest)* })
};
($target:ident $scope:ident empty { for $for_pattern:pat in $($rest:tt)* }) => {
$crate::using_impl!($target $scope in_for ($for_pattern) () { $($rest)* })
};
($target:ident $scope:ident in_for
($for_pattern:pat)
($($for_exp:tt)*)
{ { $($body:tt)* } $($rest:tt)* }
) => {
{
for $for_pattern in $($for_exp)* {
$crate::using_impl!($target block empty { $($body)* })
}
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident in_for
($for_pattern:pat)
($($for_exp:tt)*)
{ $t:tt $($rest:tt)* }
) => {
$crate::using_impl!($target $scope in_for ($for_pattern) ($($for_exp)* $t) { $($rest)* })
};
($target:ident $scope:ident empty { $st:stmt; $($rest:tt)* }) => {
{
$st
$crate::using_impl!($target $scope empty { $($rest)* })
}
};
($target:ident $scope:ident empty { $exp:expr }) => {
$exp
};
}
#[cfg(test)]
mod tests {
use crate::using;
#[test]
fn simple() {
let vec = using!(Vec::new() => {
.push(1);
.push(2);
.push(3);
.push(4);
.push(5);
});
assert_eq!(vec.iter().sum::<i32>(), 15);
}
#[test]
fn simple_expr() {
let sum = using!(Vec::new() => {
.push(1);
.push(2);
.push(3);
.push(4);
.push(5);
.iter().sum::<i32>()
});
assert_eq!(sum, 15);
}
#[test]
fn block_expr() {
let sum: i32 = using!(Vec::new() => {
.push(1);
{
.push(2);
.push(3);
}
.push(4);
{
.push(5);
.iter().sum()
}
});
assert_eq!(sum, 15);
}
#[test]
fn if_expr() {
for i in 0..3 {
let res = using!(Vec::new() => {
if let 0 = i {
.push(0);
} else if i == 1 {
.push(1);
} else {
.push(2);
}
.pop().unwrap()
});
assert_eq!(res, i);
}
}
#[test]
fn match_expr() {
for i in 0..9 {
let res = using!(vec @ Vec::new() => {
match i {
0 => .push(0),
1 => vec.push(1),
2 => { .push(2) }
3 => { .push(3) },
4 if true => .push(4),
5 if true => vec.push(5),
6 if true => { .push(6) }
7 if true => { .push(7) },
_ => { .push(8) }
}
.pop().unwrap()
});
assert_eq!(res, i);
}
}
#[test]
fn loop_expr() {
let sum: i32 = using!(Vec::new() => {
let mut i = 1;
loop {
if i > 5 {
break;
}
.push(i);
i += 1;
}
.iter().sum()
});
assert_eq!(sum, 15);
}
#[test]
fn while_loop() {
let sum: i32 = using!(Vec::new() => {
let mut i = 1;
while i <= 5 {
.push(i);
i += 1;
}
.iter().sum()
});
assert_eq!(sum, 15);
}
#[test]
fn while_let() {
let sum: i32 = using!(Vec::new() => {
let mut i = 1;
while let Some(_) = (i <= 5).then_some(i) {
.push(i);
i += 1;
}
.iter().sum()
});
assert_eq!(sum, 15);
}
#[test]
fn for_loop() {
let sum: i32 = using!(Vec::new() => {
for i in 1..=5 {
.push(i);
}
.iter().sum()
});
assert_eq!(sum, 15);
}
#[test]
fn if_in_for() {
let sum: i32 = using!(Vec::new() => {
for i in 1..=10 {
if i % 2 == 0 {
.push(i);
}
}
.iter().sum()
});
assert_eq!(sum, 30);
}
#[test]
fn let_exp() {
let sum: i32 = using!(Vec::new() => {
.push(1);
.push(2);
.push(3);
let sum = .iter().sum();
.push(sum);
let res = { .pop().unwrap() };
2 * res
});
assert_eq!(sum, 12);
}
#[test]
fn let_complex() {
let res = using!(Vec::new() => {
.push(2);
.push(3);
.push(5);
let a = loop { let x = .last().unwrap(); break *x };
let b = if a < 10 { .first().is_some() } else { .is_empty() };
let c = match b { true => .len(), false => 0 };
(a, b, c)
});
assert_eq!(res, (5, true, 3));
}
#[test]
fn nested_using() {
let sum: i32 = using!(Vec::new() => {
.push(1);
.push(2);
.push(3);
.push(4);
.push(5);
.push(using!(Vec::new() => {
.push(2);
.push(3);
.iter().product()
}));
.iter().sum()
});
assert_eq!(sum, 21);
}
}