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);
pub trait ParamAndAssociatedConst<const N: usize> {
const VALUE: usize;
const LESS: usize = Self::VALUE - N;
const GREATER: usize = N - Self::VALUE;
const ASSERT_EQ: (usize, usize) = (Self::LESS, Self::GREATER);
}
#[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! main_alphabet {
($len:expr; $($cl:ident),+ $(,)?) => { paste!{
#[doc=concat!($len, "-ary tuple wrapper")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct [<T $len>]<$($cl,)+>($(pub $cl,)+);
impl<T, const N: usize, $(const [<$cl N>]:usize,)+> ParamAndAssociatedConst<N>
for [<T $len>]<$([T;[<$cl N>]],)+>
{
const VALUE: usize = 0 $(+ [<$cl N>])+;
}
impl<$([<$cl 0>],[<$cl 1>],)+> From<[<T $len>]<$([<$cl 0>],)+>> for ($([<$cl 1>],)+)
where
$([<$cl 1>]: From<[<$cl 0>]>,)+
{
fn from([<T $len>]($([<$cl:lower>],)+): [<T $len>]<$([<$cl 0>],)+>) -> Self {
( $([<$cl:lower>].into(),)+ )
}
}
impl<$([<$cl 0>],[<$cl 1>],)+> From<($([<$cl 0>],)+)> for [<T $len>]<$([<$cl 1>],)+>
where
$([<$cl 1>]: From<[<$cl 0>]>,)+
{
fn from(($([<$cl:lower>],)+): ($([<$cl 0>],)+)) -> Self {
Self( $([<$cl:lower>].into(),)+ )
}
}
impl<T, const N: usize, $(const [<$cl N>]:usize,)+>
From<[T; N]> for [<T $len>]<$([T;[<$cl N>]],)+>
where
T: Default + Copy,
{
fn from(data: [T; N]) -> Self {
#![allow(path_statements)]
<Self as ParamAndAssociatedConst::<N>>::ASSERT_EQ;
let end = 0;
$(
let mut [<$cl:lower>] = [Default::default();[<$cl N>]];
let (start, end) = (end, end + [<$cl N>]);
[<$cl:lower>].copy_from_slice(&data[start..end]);
)+
Self($([<$cl:lower>],)+)
}
}
#[doc=concat!("Type wrapper with ", $len, " const generic parameters")]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct [<P $len>]<TY, $(const $cl: usize,)+>(pub TY);
impl<TY, $(const [<$cl N>]: usize,)+> [<P $len>]<TY,$([<$cl N>],)+> {
pub const SUM: usize = 0 $(+ [<$cl N>])+;
}
impl<TY, $($cl,)+ const NU: usize, $(const [<$cl N>]: usize,)+>
From<[<P $len>]<[TY;NU],$([<$cl N>],)+>> for ($($cl,)+)
where
TY: Copy + Default,
$($cl: From<[TY;[<$cl N>]]>,)+
{
fn from([<P $len>](data): [<P $len>]<[TY;NU],$([<$cl N>],)+>) -> Self {
[<T $len>]::from(data).into()
}
}
impl<'a, T, $(const [<$cl N>]:usize,)+> TryFrom<&'a [T]>
for Seq<[<T $len>]<$([T;[<$cl N>]],)+>, &'a [T]>
where
T: Copy,
{
type Error = TryFromSliceError;
fn try_from(slice: &'a [T]) -> Result<Self, Self::Error> {
$(
let Seq { head: [<$cl:lower>], tail: slice }: Seq<_, &[T]> =
slice.try_into()?;
)+
Ok(Self {
head: [<T $len>]($([<$cl:lower>],)+),
tail: slice,
})
}
}
impl<'a, T, U, $(const [<$cl N>]: usize,)+>
TryFrom<[<P $len>]<&'a [T], $([<$cl N>],)+>> for Seq<U, &'a [T]>
where
T: Copy,
U: From<[<T $len>]<$([T;[<$cl N>]],)+>>
{
type Error = TryFromSliceError;
fn try_from([<P $len>](data): [<P $len>]<&'a [T], $([<$cl N>],)+>) ->
Result<Self, Self::Error>
{
data.try_into().map(|Seq { head, tail }| Seq {
head: From::<[<T $len>]<$([T; [<$cl N>]],)+>>::from(head),
tail,
})
}
}
}};
}
main_alphabet!(1; A);
main_alphabet!(2; A,B);
main_alphabet!(3; A,B,C);
main_alphabet!(4; A,B,C,D);
main_alphabet!(5; A,B,C,D,E);
main_alphabet!(6; A,B,C,D,E,F);
main_alphabet!(7; A,B,C,D,E,F,G);
main_alphabet!(8; A,B,C,D,E,F,G,H);
main_alphabet!(9; A,B,C,D,E,F,G,H,I);
main_alphabet!(10; A,B,C,D,E,F,G,H,I,J);
main_alphabet!(11; A,B,C,D,E,F,G,H,I,J,K);
main_alphabet!(12; A,B,C,D,E,F,G,H,I,J,K,L);
main_alphabet!(13; A,B,C,D,E,F,G,H,I,J,K,L,M);
main_alphabet!(14; A,B,C,D,E,F,G,H,I,J,K,L,M,N);
main_alphabet!(15; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O);
main_alphabet!(16; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P);
main_alphabet!(17; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q);
main_alphabet!(18; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R);
main_alphabet!(19; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S);
main_alphabet!(20; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T);
main_alphabet!(21; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U);
main_alphabet!(22; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V);
main_alphabet!(23; A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W);
main_alphabet!(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);
main_alphabet!(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);
main_alphabet!(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());
}
}