use std::any::Any;
#[doc(inline)]
pub trait TupleCombinator: Sized {
type Tuple;
fn transpose(self) -> Option<Self::Tuple>;
fn map<U, F: FnOnce(Self::Tuple) -> U>(self, f: F) -> Option<U> {
self.transpose().map(f)
}
fn expect(self, msg: &str) -> Self::Tuple {
self.transpose().expect(msg)
}
fn unwrap(self) -> Self::Tuple {
self.transpose().unwrap()
}
fn and(self, optb: Option<Self::Tuple>) -> Option<Self::Tuple> {
self.transpose().and(optb)
}
fn and_then<U, F: FnOnce(Self::Tuple) -> Option<U>>(self, f: F) -> Option<U> {
self.transpose().and_then(f)
}
fn filter<P: FnOnce(&Self::Tuple) -> bool>(self, predicate: P) -> Option<Self::Tuple> {
self.transpose().filter(predicate)
}
fn or(self, optb: Option<Self::Tuple>) -> Option<Self::Tuple> {
self.transpose().or(optb)
}
fn or_else<F: FnOnce() -> Option<Self::Tuple>>(self, f: F) -> Option<Self::Tuple> {
self.transpose().or_else(f)
}
fn xor(self, optb: Option<Self::Tuple>) -> Option<Self::Tuple> {
self.transpose().xor(optb)
}
}
#[doc(inline)]
pub trait TupleReducer: Sized {
fn fold<U, F: Fn(Option<U>, &dyn Any) -> Option<U>>(&self, init: U, f: F) -> Option<U>;
fn fold_strict<U: Any, F: Fn(Option<U>, &U) -> Option<U>>(&self, init: U, f: F) -> Option<U>;
fn ref_slice(&self) -> Box<[&dyn Any]>;
fn strict_ref_slice<T: Any>(&self) -> Box<[&Option<T>]>;
fn mut_slice(&mut self) -> Box<[&mut dyn Any]>;
fn strict_mut_slice<T: Any>(&mut self) -> Box<[&mut Option<T>]>;
}
macro_rules! tuple_impls {
( $( $v:ident: $T:ident, )* ) => {
impl<$($T,)*> TupleCombinator for ($(Option<$T>,)*) {
type Tuple = ($($T,)*);
fn transpose(self) -> Option<Self::Tuple> {
if let ($(Some($v),)*) = self {
Some(($($v,)*))
} else {
None
}
}
}
};
}
macro_rules! tuple_impl_reduce {
() => {};
( $( $ntyp:ident => $nidx:tt, )+ ) => {
impl<$( $ntyp, )+> TupleReducer for ( $( Option<$ntyp>, )+ )
where
$( $ntyp: Any, )*
{
fn fold<U, F: Fn(Option<U>, &dyn Any) -> Option<U>>(&self, init: U, f: F) -> Option<U> {
let mut accu = Some(init);
$(
accu = f(accu, &self.$nidx);
)*
accu
}
fn fold_strict<U: Any, F: Fn(Option<U>, &U) -> Option<U>>(&self, init: U, f: F) -> Option<U> {
let mut accu = Some(init);
$(
let opt = (&self.$nidx as &dyn Any)
.downcast_ref::<Option<U>>()
.and_then(|opt| opt.as_ref());
if let Some(value) = opt {
accu = f(accu, value);
}
)*
accu
}
fn ref_slice(&self) -> Box<[&dyn Any]> {
let mut vec: Vec<&dyn Any> = Vec::with_capacity(12);
$(
vec.push(&self.$nidx);
)*
vec.into_boxed_slice()
}
fn strict_ref_slice<T: Any>(&self) -> Box<[&Option<T>]> {
let mut vec: Vec<&Option<T>> = Vec::with_capacity(12);
$(
(&self.$nidx as &dyn Any)
.downcast_ref::<Option<T>>()
.and_then(|opt| {
vec.push(opt);
Some(())
});
)*
vec.into_boxed_slice()
}
fn mut_slice(&mut self) -> Box<[&mut dyn Any]> {
let mut vec: Vec<&mut dyn Any> = Vec::with_capacity(12);
$(
vec.push(&mut self.$nidx);
)*
vec.into_boxed_slice()
}
fn strict_mut_slice<T: Any>(&mut self) -> Box<[&mut Option<T>]> {
let mut vec: Vec<&mut Option<T>> = Vec::with_capacity(12);
$(
(&mut self.$nidx as &mut dyn Any)
.downcast_mut::<Option<T>>()
.and_then(|opt| {
vec.push(opt);
Some(())
});
)*
vec.into_boxed_slice()
}
}
};
}
tuple_impls! { t1: T1, }
tuple_impls! { t1: T1, t2: T2, }
tuple_impls! { t1: T1, t2: T2, t3: T3, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, }
tuple_impls! { t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, }
tuple_impl_reduce! { T0 => 0, }
tuple_impl_reduce! { T0 => 0, T1 => 1, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, }
tuple_impl_reduce! { T0 => 0, T1 => 1, T2 => 2, T3 => 3, T4 => 4, T5 => 5, T6 => 6, T7 => 7, T8 => 8, T9 => 9, }
#[cfg(test)]
mod impl_tests {
use super::TupleReducer;
use std::any::Any;
#[test]
fn fold_sum() {
let res = (Some(17), Some(20)).fold(5, |sum, item| {
sum.and_then(|s| {
item.downcast_ref::<Option<i32>>()
.and_then(|raw| raw.as_ref())
.and_then(|val| Some(s + val))
})
});
assert_eq!(res, Some(42));
}
#[test]
fn fold_mixed() {
let res = (
Some(1),
Some(5),
Some("rust_tuple"),
Some(String::from("tuple_reducer")),
Some(vec![0u8, 1, 42]), ).fold(0, |sum, item| {
sum.and_then(|s| {
if let Some(raw_i32) = item.downcast_ref::<Option<i32>>() {
return raw_i32.as_ref().and_then(|val| Some(s + val));
}
if let Some(raw_str) = item.downcast_ref::<Option<&str>>() {
return raw_str.as_ref().and_then(|val| Some(s + val.len() as i32));
}
if let Some(raw_string) = item.downcast_ref::<Option<String>>() {
return raw_string.as_ref().and_then(|val| Some(s + val.len() as i32));
}
if let Some(raw_vec) = item.downcast_ref::<Option<Vec<u8>>>() {
return raw_vec.as_ref().and_then(|val| Some(s + val.len() as i32));
}
Some(s)
})
});
assert_eq!(res, Some(32));
}
#[test]
fn fold_none_as_nuke() {
let none: Option<i32> = None;
let res = (Some(1), none, Some(5)).fold(0, |sum, item| {
sum.and_then(|s| {
item.downcast_ref::<Option<i32>>()
.and_then(|raw| raw.as_ref())
.and_then(|val| Some(s + val))
})
});
assert_eq!(res, None);
}
#[test]
fn fold_none_as_reset() {
let none: Option<i32> = None;
let init = 0;
let res = (Some(1), none, Some(5)).fold(init, |sum, item| {
item.downcast_ref::<Option<i32>>()
.and_then(|raw| raw.as_ref())
.and_then(|val| {
if let Some(s) = sum {
Some(s + val)
} else {
Some(init + val)
}
})
});
assert_eq!(res, Some(5));
}
#[test]
fn fold_strict_base() {
let res = (Some(40), Some("noise"), None as Option<i32>, Some(2))
.fold_strict(0i32, |sum, item| {
sum.and_then(|s| {
Some(s + item)
})
});
assert_eq!(res, Some(42));
}
#[test]
fn ref_slice_base() {
let src = (Some(1), None as Option<&str>, Some(2), None as Option<i32>, Some(()));
let slice: Box<[&dyn Any]> = src.ref_slice();
assert_eq!(slice.len(), 5);
assert_eq!(slice[0].downcast_ref::<Option<i32>>().unwrap(), &Some(1));
assert_eq!(slice[0].downcast_ref::<Option<&str>>(), None);
assert_eq!(slice[1].downcast_ref::<Option<&str>>().unwrap(), &None);
}
#[test]
fn strict_ref_slice_base() {
let src = (Some(1), None as Option<&str>, Some(2), None as Option<i32>, Some(()));
let slice = src.strict_ref_slice::<i32>();
assert_eq!(slice.len(), 3);
assert_eq!(
slice,
vec![&Some(1), &Some(2), &None as &Option<i32>].into_boxed_slice()
);
}
#[test]
fn mut_slice_base() {
let mut src = (Some(1), None as Option<&str>, Some(2), None as Option<i32>, Some(()));
let slice: Box<[&mut dyn Any]> = src.mut_slice();
assert_eq!(slice.len(), 5);
assert_eq!(slice[0].downcast_ref::<Option<i32>>().unwrap(), &Some(1));
assert_eq!(slice[1].downcast_ref::<Option<&str>>().unwrap(), &None);
let first = slice[0].downcast_mut::<Option<i32>>().unwrap().take();
assert_eq!(first, Some(1));
}
#[test]
fn strict_mut_slice_base() {
let mut src = (Some(1), None as Option<&str>, Some(2), None as Option<i32>, Some(()));
let slice = src.strict_mut_slice::<i32>();
assert_eq!(slice.len(), 3);
assert_eq!(
slice,
vec![&mut Some(1), &mut Some(2), &mut None as &mut Option<i32>].into_boxed_slice()
);
let first = slice[0].take();
assert_eq!(first, Some(1));
assert_eq!(slice[0], &mut None);
}
}