#![no_std]
use core::{
mem::{self, MaybeUninit},
ptr,
};
#[doc(hidden)]
pub type Usize = usize;
#[doc(hidden)]
pub use core::{iter::IntoIterator, num::Wrapping, ops::Not};
#[doc(hidden)]
pub struct DontBreakFromElementExpressionWithoutLabel;
#[doc(hidden)]
pub fn type_name_of_val<T: ?Sized>(_val: &T) -> &'static str {
::core::any::type_name::<T>()
}
#[doc(hidden)]
pub struct PartiallyInitArray<T, const N: usize> {
array: [MaybeUninit<T>; N],
init: usize,
}
impl<T, const N: usize> PartiallyInitArray<T, N> {
pub fn uninit() -> Self {
PartiallyInitArray {
array: unsafe {
MaybeUninit::uninit().assume_init()
},
init: 0,
}
}
#[inline]
pub unsafe fn write(&mut self, value: T) {
debug_assert!(self.init < N);
self.array[self.init].write(value);
self.init += 1;
}
#[inline]
pub fn is_init(&self) -> bool {
self.init == N
}
#[inline]
pub unsafe fn assume_init(self) -> [T; N] {
debug_assert_eq!(self.init, N);
let array = {
mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&self.array)
};
mem::forget(self);
array
}
#[inline]
pub fn try_init(self) -> Option<[T; N]> {
if self.init == N {
let array = unsafe {
mem::transmute_copy::<[MaybeUninit<T>; N], [T; N]>(&self.array)
};
mem::forget(self);
Some(array)
} else {
None
}
}
}
impl<T, const N: usize> Drop for PartiallyInitArray<T, N> {
fn drop(&mut self) {
let slice = &mut self.array[..self.init];
unsafe { ptr::drop_in_place(slice as *mut [MaybeUninit<T>] as *mut [T]) }
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! pattern_list {
($ph:pat, $($pt:pat,)*) => {
$crate::pattern_list!($($pt,)* ; $ph )
};
($ph:pat, $($pt:pat,)* ; $r:pat) => {
$crate::pattern_list!($($pt,)* ; ($r, $ph) )
};
(; $r:pat) => {
$r
};
}
#[macro_export]
macro_rules! array {
($($e:expr),* $(,)?) => { [$($e,)*] };
($e:expr; $n:expr) => {{
$crate::array!( _ => $e ; $n )
}};
($p:pat => $e:expr $( ; where $( $cond:expr ),+ )? ; $n:expr) => {{
#[allow(unused_mut)]
let mut array = $crate::PartiallyInitArray::<_, $n>::uninit();
let mut i = $crate::Wrapping(0);
loop {
let value = i.0;
i += 1;
if i.0 == 0 {
panic!("Failed to initialize array using whole '{}' space", $crate::type_name_of_val(&i.0));
}
if array.is_init() {
break;
}
match value {
$p => {
#[allow(unreachable_code)]
{
$($(
#[allow(unused_variables)]
#[warn(unreachable_code)]
let cond = $cond;
if <bool as $crate::Not>::not(cond) { continue; }
)+)?
#[allow(unused_variables)]
let elem;
#[allow(unused_variables)]
let dont_continue_in_element_expression_without_label;
loop {
#[allow(unused)]
{
dont_continue_in_element_expression_without_label = ();
}
#[allow(unused_variables)]
#[warn(unreachable_code)]
let value = $e;
elem = value;
break $crate::DontBreakFromElementExpressionWithoutLabel;
};
unsafe {
array.write(elem);
}
}
}
#[allow(unreachable_patterns)]
_ => continue,
}
}
unsafe {
array.assume_init()
}
}};
}
#[macro_export]
macro_rules! collect_array {
($it:expr; $n:expr) => {
$crate::collect_array!(e in $it => e ; $n)
};
($e:expr; $ph:pat in $ih:expr $( , $pt:pat in $it:expr )* $(; where $($cond:expr),+ )? ; $n:expr) => {{
#[allow(unused_mut)]
let mut array = $crate::PartiallyInitArray::<_, $n>::uninit();
let iter = $crate::IntoIterator::into_iter($ih);
$( let iter = iter.zip($it); )*
let mut iter = iter;
loop {
if array.is_init() {
break;
}
match iter.next() {
None => break,
Some($crate::pattern_list!($ph, $( $pt, )*)) => {
#[allow(unreachable_code)]
{
$($(
#[allow(unused_variables)]
#[warn(unreachable_code)]
let cond = $cond;
if <bool as $crate::Not>::not(cond) { continue; }
)+)?
#[allow(unused_variables)]
let elem;
#[allow(unused_variables)]
let dont_continue_in_element_expression_without_label;
loop {
#[allow(unused)]
{
dont_continue_in_element_expression_without_label = ();
}
#[allow(unused_variables)]
#[warn(unreachable_code)]
let value = $e;
elem = value;
break $crate::DontBreakFromElementExpressionWithoutLabel;
};
unsafe {
array.write(elem);
}
}
}
#[allow(unreachable_patterns)]
_ => continue,
}
}
array.try_init()
}};
($( $p:pat in $i:expr ),+ => $e:expr $(; where $($cond:expr),+ )? ; $n:expr) => {
$crate::collect_array!($e; $($p in $i),+ $( ; where $($cond),+ )? ; $n)
};
}
#[test]
fn test_expression_repeat() {
let mut i = 0;
assert_eq!(array!({ i+=1; i }; 2), [1, 2]);
}
#[test]
fn test_comprehension_repeat() {
assert_eq!(array!(x => x * 2; 3), [0, 2, 4]);
assert_eq!(array!(x => x * 2; where x & 1 == 1; 3), [2, 6, 10]);
}
#[test]
fn test_comprehension_iter() {
assert_eq!(
collect_array!(x * 2; x in 1..3; 3),
None,
"There's not enough elements in iterator"
);
assert_eq!(
collect_array!(x * 2; x in 1..; 3),
Some([2, 4, 6]),
"1*2, 2*2, 3*2"
);
assert_eq!(
collect_array!(x * y; x in 1.., y in (1..3).cycle(); where x > 3, y == 1; 3),
Some([5, 7, 9]),
"x = 1,2,3,4,5,6,7,8,9
y = 1,2,1,2,1,2,1,2,1
r = x,x,x,x,5,x,7,x,9"
);
assert_eq!(
collect_array!(x in 0.. => x * 2; where x & 1 == 1; 3),
Some(array!(x => x * 2; where x & 1 == 1; 3)),
);
assert_eq!(
collect_array!(x in 0.., _y in 1.., _z in 2.., _w in 3..5 => x; where x & 1 == 1; 3),
None,
);
}
#[test]
fn test_bail() {
array!(return; 2);
panic!();
}
#[test]
fn test_bail_condition() {
array!(_ => 0; where return; 1);
panic!();
}
#[test]
fn test_bail_iter() {
collect_array!(_ in 1.. => 0; where return; 1);
panic!();
}
#[test]
#[should_panic]
fn test_bail_panic() {
array!(return; 0);
panic!();
}
#[test]
#[should_panic]
fn test_bail_condition_panic() {
array!(_ => 0; where return; 0);
panic!();
}