use core::array::TryFromSliceError;
use funty::Fundamental;
use paste::paste;
pub trait AsPrimitive<T> {
#[allow(clippy::wrong_self_convention)]
fn as_primitive(self) -> T;
}
impl<T: Fundamental> AsPrimitive<Option<char>> for T {
fn as_primitive(self) -> Option<char> {
self.as_char()
}
}
impl<T> AsPrimitive<()> for T {
fn as_primitive(self) {}
}
macro_rules! main_impl_for {
( AsPrimitive => $($cl:ty),+ $(,)?) => {paste!{ $(
impl<T: Fundamental> AsPrimitive<$cl> for T {
fn as_primitive(self) -> $cl { self.[<as_ $cl>]() }
}
)+ }};
}
main_impl_for!(AsPrimitive => bool,u8,u16,u32,u64,u128,usize);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Seq<H, T> {
pub head: H,
pub tail: T,
}
impl<'a, T, const N: usize> TryFrom<&'a [T]> for Seq<[T; N], &'a [T]>
where
T: Copy,
{
type Error = TryFromSliceError;
fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
let (head, tail) = slice.split_at(slice.len().min(N));
Ok(Self {
head: head.try_into()?,
tail,
})
}
}
macro_rules! common_impl {
($len:expr; $_:ident $(,)? $($cl:ident),* $(,)?) => { paste!{
#[doc=concat!($len, "-ary tuple wrapper")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct [<T $len>]<A, $($cl, )*>(pub A, $(pub $cl, )*);
impl<A0, A1, $([<$cl 0>], [<$cl 1>], )*> From<[<T $len>]<A0, $([<$cl 0>], )*>> for (A1, $([<$cl 1>], )*)
where
A1: From<A0>, $([<$cl 1>]: From<[<$cl 0>]>, )*
{
fn from([<T $len>](a, $([<$cl:lower>], )*): [<T $len>]<A0, $([<$cl 0>], )*>) -> Self {
(a.into(), $([<$cl:lower>].into(), )*)
}
}
impl<A0, A1, $([<$cl 0>], [<$cl 1>], )*> From<(A0, $([<$cl 0>], )*)> for [<T $len>]<A1, $([<$cl 1>], )*>
where
A1: From<A0>, $([<$cl 1>]: From<[<$cl 0>]>, )*
{
fn from((a, $([<$cl:lower>], )*): (A0, $([<$cl 0>], )*)) -> Self {
Self(a.into(), $([<$cl:lower>].into(), )*)
}
}
impl<T, const N: usize, const AN: usize, $(const [<$cl N>]: usize, )*>
From<[T; N]> for [<T $len>]<[T; AN], $([T; [<$cl N>]],)*>
where
T: Default + Copy,
{
fn from(data: [T; N]) -> Self {
const {
const MSG: &str =
concat!("The sum ", stringify!(AN $(+ [<$cl N>])*), " should be equal to N");
assert!(AN $(+ [<$cl N>])* == N, "{}", MSG);
}
let end = 0;
let mut a = [Default::default(); AN];
let (start, end) = (end, end + AN);
a.copy_from_slice(&data[start..end]);
$(
let mut [<$cl:lower>] = [Default::default();[<$cl N>]];
let (start, end) = (end, end + [<$cl N>]);
[<$cl:lower>].copy_from_slice(&data[start..end]);
)*
Self(a, $([<$cl:lower>], )*)
}
}
#[doc=concat!("Type wrapper with ", $len, " const generic parameters")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct [<P $len>]<TY, const A: usize, $(const $cl: usize, )*>(pub TY);
impl<TY, const AN: usize, $(const [<$cl N>]: usize, )*> [<P $len>]<TY, AN, $([<$cl N>], )*> {
pub const SUM: usize = AN $(+ [<$cl N>])*;
}
impl<TY, A, $($cl, )* const NU: usize, const AN: usize, $(const [<$cl N>]: usize, )*>
From<[<P $len>]<[TY; NU], AN, $([<$cl N>], )*>> for (A, $($cl, )*)
where
TY: Copy + Default,
A: From<[TY; AN]>, $($cl: From<[TY;[<$cl N>]]>, )*
{
fn from([<P $len>](data): [<P $len>]<[TY; NU], AN, $([<$cl N>], )*>) -> Self {
[<T $len>]::from(data).into()
}
}
impl<'a, T, const AN: usize, $(const [<$cl N>]: usize, )*> TryFrom<&'a [T]>
for Seq<[<T $len>]<[T; AN], $([T; [<$cl N>]], )*>, &'a [T]>
where
T: Copy,
{
type Error = TryFromSliceError;
fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
let Seq { head: a, tail: slice }: Seq<_, &[T]> = slice.try_into()?;
$(
let Seq { head: [<$cl:lower>], tail: slice }: Seq<_, &[T]> =
slice.try_into()?;
)*
Ok(Self {
head: [<T $len>](a, $([<$cl:lower>], )*),
tail: slice,
})
}
}
impl<'a, T, U, const AN: usize, $(const [<$cl N>]: usize, )*>
TryFrom<[<P $len>]<&'a [T], AN, $([<$cl N>], )*>> for Seq<U, &'a [T]>
where
T: Copy,
U: From<[<T $len>]<[T; AN], $([T;[<$cl N>]], )*>>
{
type Error = TryFromSliceError;
fn try_from([<P $len>](data): [<P $len>]<&'a [T], AN, $([<$cl N>], )*>) ->
Result<Self, Self::Error>
{
data.try_into().map(|Seq { head, tail }| Seq {
head: From::<[<T $len>]<[T; AN], $([T; [<$cl N>]], )*>>::from(head),
tail,
})
}
}
}};
}
common_impl!(1; A);
common_impl!(2; A,B);
common_impl!(3; A,B,C);
common_impl!(4; A,B,C,D);
common_impl!(5; A,B,C,D,E);
common_impl!(6; A,B,C,D,E,F);
common_impl!(7; A,B,C,D,E,F,G);
common_impl!(8; A,B,C,D,E,F,G,H);
common_impl!(9; A,B,C,D,E,F,G,H,I);
common_impl!(10; A,B,C,D,E,F,G,H,I,J);
common_impl!(11; A,B,C,D,E,F,G,H,I,J,K);
common_impl!(12; A,B,C,D,E,F,G,H,I,J,K,L);
common_impl!(13; A,B,C,D,E,F,G,H,I,J,K,L,M);
common_impl!(14; A,B,C,D,E,F,G,H,I,J,K,L,M,N);
common_impl!(15; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O);
common_impl!(16; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P);
common_impl!(17; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q);
common_impl!(18; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R);
common_impl!(19; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S);
common_impl!(20; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T);
common_impl!(21; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U);
common_impl!(22; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V);
common_impl!(23; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W);
common_impl!(24; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X);
common_impl!(25; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y);
common_impl!(26; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z);
macro_rules! impl_wrappers_as_primitive {
($($ty:ty),+ $(,)?) => {paste!{ $(
#[doc=concat!("Wrapper around type that may be converted from [", stringify!([<$ty:lower>]), "]")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct $ty<T: From<[<$ty:lower>]>>(pub T);
impl<T: From<[<$ty:lower>]>, U: Fundamental> AsPrimitive<$ty<T>> for U {
fn as_primitive(self) -> $ty<T> {
$ty(self.[<as_ $ty:lower>]().into())
}
}
)+ }};
}
impl_wrappers_as_primitive!(Bool, U8, U16, U32, U64, U128, Usize);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn split_array() {
let chars = ['a', 'b', 'b'];
assert_eq!(T2(['a'], ['b', 'b']), T2::from(chars));
let bytes = [1u8, 2, 2, 3, 3, 3, 3];
assert_eq!(T3([1], [2, 2], [3, 3, 3, 3]), T3::from(bytes));
}
#[test]
fn tuple_of_elements_from() {
assert_eq!((97u128, 98usize), T2(97u8, 98u16).into());
assert_eq!(('a', 'b', 'c'), T3(97, 98, 99).into());
}
#[test]
fn tuple_of_elements_into() {
assert_eq!(T2(97u32, 98i32), (97u8, 98u16).into());
assert_eq!(T3('a', 'b', 'c'), (97, 98, 99).into());
}
#[test]
fn slice_try_into_tuple_of_arrays() {
let bytes = [1u8, 2, 2, 3, 3, 3, 3];
let Seq {
head: T3(a, b, c), ..
} = bytes[..].try_into().unwrap();
assert_eq!(([1], [2, 2], [3, 3, 3]), (a, b, c));
}
#[test]
fn partition_ready_longer_slice_try_into() {
let bytes = [1u8, 2, 2, 3, 3, 3, 3, 42];
let result = bytes.as_slice().try_into().ok();
let sample = Some(Seq {
head: T3([1], [2, 2], [3, 3, 3, 3]),
tail: &bytes[7..],
});
assert_eq!(sample, result, "tuple warpper");
let result = P3(bytes.as_slice()).try_into().ok();
let sample = Some(Seq {
head: ([1], [2, 2], [3, 3, 3, 3]),
tail: &bytes[7..],
});
assert_eq!(sample, result, "tuple of arrays");
}
#[test]
fn partition_ready_exact_slice_try_into() {
let bytes = [1u8, 2, 2, 3, 3, 3, 3];
let result = bytes.as_slice().try_into().ok();
let sample = Some(Seq {
head: T3([1], [2, 2], [3, 3, 3, 3]),
tail: [].as_slice(),
});
assert_eq!(sample, result, "tuple warpper");
let result = P3(bytes.as_slice()).try_into().ok();
let sample = Some(Seq {
head: ([1], [2, 2], [3, 3, 3, 3]),
tail: [].as_slice(),
});
assert_eq!(sample, result, "tuple of arrays");
}
#[test]
fn partition_ready_shorter_slice_try_into() {
let bytes = [1u8, 2, 2, 3, 3];
#[allow(clippy::type_complexity)]
let result: Option<Seq<T3<[_; 1], [_; 2], [_; 3]>, &[u8]>> =
bytes.as_slice().try_into().ok();
let sample = None;
assert_eq!(sample, result, "slice is shorter");
#[allow(clippy::type_complexity)]
let result: Option<Seq<([_; 1], [_; 2], [_; 3]), &[u8]>> =
P3(bytes.as_slice()).try_into().ok();
let sample = None;
assert_eq!(sample, result, "slice is shorter");
}
#[test]
fn const_generic_params_sum() {
assert_eq!(6, P3::<u8, 1, 2, 3>::SUM);
}
#[test]
fn wrappers_as_primitives() {
#[derive(Debug, Clone, PartialEq, Eq)]
enum En {
One,
}
impl From<u8> for En {
fn from(_: u8) -> Self {
Self::One
}
}
assert_eq!(U8(En::One), 0u8.as_primitive());
assert_eq!(U8(En::One), 0u16.as_primitive());
assert_eq!(U8(En::One), 0u32.as_primitive());
assert_eq!(U8(En::One), 0u64.as_primitive());
assert_eq!(U8(En::One), 0u128.as_primitive());
assert_eq!(U8(En::One), 0usize.as_primitive());
assert_eq!(U8(En::One), false.as_primitive());
}
}