array-fusion 0.2.0

Array merging and splitting facilities
Documentation
use core::ops::Add;
use core::ops::Mul;
use core::ops::Sub;

pub use hybrid_array;
#[doc(hidden)]
pub use hybrid_array::Array;
use hybrid_array::ArraySize;
use hybrid_array::AssocArraySize;
#[doc(hidden)]
pub use hybrid_array::sizes;
use hybrid_array::sizes::U0;
use hybrid_array::sizes::U1;
use hybrid_array::typenum::Prod;
use hybrid_array::typenum::Sum;

use super::SizeOf;
use crate::array_to_core;
use crate::chunks::chunks;



#[macro_export]
macro_rules! split_bytes {
	(
		let bytes [
			$($name:ident $(: $ty:ty)? ),* $(,)?
		] = $data:expr $(;)?
	) => {
		$crate::split_array!{
			let split [
				$($name),*
			] = $data;
		}

		$(
			let $name $( : $ty )? = $crate::bytes::decoding::FromByteArray::from_bytes($name) ;
		)*
	};
}



pub trait FromByteArray {
	type Size: ArraySize;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self;
}


impl<Size, T> FromByteArray for Array<T, Size>
where
	Size: ArraySize,
	T: FromByteArray,
	Prod<T::Size, Size>: ArraySize,
	T::Size: Mul<Size>,
{
	type Size = Prod<T::Size, Size>;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		chunks(array).map(|sub| FromByteArray::from_bytes(sub))
	}
}

impl<const N: usize, T> FromByteArray for [T; N]
where
	T: FromByteArray,
	[T; N]: AssocArraySize,
	<[T; N] as AssocArraySize>::Size: ArraySize<ArrayType<T> = [T; N]>,
	Prod<T::Size, SizeOf<T, N>>: ArraySize,
	T::Size: Mul<SizeOf<T, N>>,
{
	type Size = Prod<T::Size, SizeOf<T, N>>;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		array_to_core(Array::from_bytes(array))
	}
}

impl FromByteArray for u8 {
	type Size = U1;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		let [n] = array.0;
		n
	}
}
impl FromByteArray for i8 {
	type Size = U1;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		let [n] = array.0;
		n as i8
	}
}

impl FromByteArray for () {
	type Size = U0;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		let [] = array.0;
	}
}
impl<T1> FromByteArray for (T1,)
where
	T1: FromByteArray,
{
	type Size = T1::Size;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		(T1::from_bytes(array),)
	}
}
impl<T1, T2> FromByteArray for (T1, T2)
where
	T1: FromByteArray,
	T2: FromByteArray,
	T1::Size: Add<T2::Size>,
	Sum<T1::Size, T2::Size>: ArraySize + Sub<T1::Size, Output = T2::Size>,
{
	type Size = Sum<T1::Size, T2::Size>;

	fn from_bytes(array: Array<u8, Self::Size>) -> Self {
		let (sub1, sub2) = array.split();
		(T1::from_bytes(sub1), T2::from_bytes(sub2))
	}
}

pub fn parse_from_array<T>(array: Array<u8, T::Size>) -> T
where
	T: FromByteArray,
{
	T::from_bytes(array)
}



#[cfg(test)]
mod test {
	use hybrid_array::sizes::U2;
	use hybrid_array::sizes::U4;

	use super::*;
	use crate::array_from_core;
	use crate::bytes::BigEndian;
	use crate::chunks::chunks_from_core;
	use crate::chunks::flatten;

	struct Foo(u16);
	impl FromByteArray for Foo {
		type Size = U2;

		fn from_bytes(array: Array<u8, Self::Size>) -> Self {
			Foo(u16::from_be_bytes(array.0))
		}
	}
	struct Bar(u32);
	impl FromByteArray for Bar {
		type Size = U4;

		fn from_bytes(array: Array<u8, Self::Size>) -> Self {
			Bar(u32::from_be_bytes(array.0))
		}
	}


	#[test]
	fn test_parse_from_array_u8() {
		let input = 42_u8;
		let data = array_from_core([input]);
		let parsed: u8 = parse_from_array(data);
		assert_eq!(parsed, input);
	}

	#[test]
	fn test_parse_from_array_id() {
		let input = [1, 2, 3, 4];
		let data = array_from_core(input);
		let parsed: Array<u8, U4> = parse_from_array(data);
		assert_eq!(parsed, Array(input));
	}

	#[test]
	fn test_parse_from_array_array() {
		let input = [1, 2, 3, 4];
		let data = array_from_core(input);
		let parsed: [u8; 4] = parse_from_array(data);
		assert_eq!(parsed, input);
	}

	#[test]
	fn test_parse_from_array_array_of_arrays() {
		let input = [[1, 2], [3, 4]];
		let data = flatten(chunks_from_core(input));
		let parsed: [[u8; 2]; 2] = parse_from_array(data);
		assert_eq!(parsed, input);
	}

	#[test]
	fn test_parse_from_array_tuple() {
		let input = [1, 2];
		let data = array_from_core(input);
		let parsed: (u8, u8) = parse_from_array(data);
		assert_eq!(parsed, (1, 2));
	}

	#[test]
	fn test_parse_from_array_custom_type() {
		let input = [0, 42];
		let data = array_from_core(input);
		let parsed: Foo = parse_from_array(data);
		assert_eq!(parsed.0, 42);
	}

	#[test]
	fn test_parse_from_array_custom_tuple() {
		let input = [0, 42, 0, 0, 0x12, 0x34];
		let data = array_from_core(input);
		let parsed: (Foo, Bar) = parse_from_array(data);
		assert_eq!(parsed.0.0, 42);
		assert_eq!(parsed.1.0, 0x1234);
	}

	#[test]
	fn test_split_bytes() {
		let data = [
			0, 42, // foo
			0x0, 0x0, 0x12, 0x34, // bar
			0,    // padding
			b'a', b'b', b'c', // alpha
		];
		split_bytes! {
			let bytes [
				foo,
				bar: BigEndian<u32>,
				_pad: u8,
				alpha: [u8; 3],
			] = array_from_core(data);
		}
		let foo: Foo = foo;
		assert_eq!(foo.0, 42);
		assert_eq!(bar.0, 0x1234);
		assert_eq!(alpha, *b"abc");
	}
}