array-fusion 0.2.0

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

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_from_core;
use crate::chunks::flatten;



#[macro_export]
macro_rules! merge_bytes {
	(
		[
			$($name:ident),* $(,)?
		]
	) => {{
		$(
			let $name = $crate::bytes::encoding::IntoByteArray::into_bytes($name) ;
		)*

		$crate::merge_array!(
			[
				$($name),*
			]
		)
	}};
}



pub trait IntoByteArray {
	type Size: ArraySize;

	fn into_bytes(self) -> Array<u8, Self::Size>;
}


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

	fn into_bytes(self) -> Array<u8, Self::Size> {
		flatten(self.map(|sub| sub.into_bytes()))
	}
}

impl<const N: usize, T> IntoByteArray for [T; N]
where
	T: IntoByteArray,
	[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 into_bytes(self) -> Array<u8, Self::Size> {
		array_from_core(self).into_bytes()
	}
}

impl IntoByteArray for u8 {
	type Size = U1;

	fn into_bytes(self) -> Array<u8, Self::Size> {
		Array([self])
	}
}
impl IntoByteArray for i8 {
	type Size = U1;

	fn into_bytes(self) -> Array<u8, Self::Size> {
		Array([self as u8])
	}
}

impl IntoByteArray for bool {
	type Size = U1;

	fn into_bytes(self) -> Array<u8, Self::Size> {
		Array([self as u8])
	}
}

impl IntoByteArray for () {
	type Size = U0;

	fn into_bytes(self) -> Array<u8, Self::Size> {
		Array([])
	}
}
impl<T1> IntoByteArray for (T1,)
where
	T1: IntoByteArray,
{
	type Size = T1::Size;

	fn into_bytes(self) -> Array<u8, Self::Size> {
		self.0.into_bytes()
	}
}
impl<T1, T2> IntoByteArray for (T1, T2)
where
	T1: IntoByteArray,
	T2: IntoByteArray,
	Sum<T1::Size, T2::Size>: ArraySize,
	T1::Size: Add<T2::Size>,
{
	type Size = Sum<T1::Size, T2::Size>;

	fn into_bytes(self) -> Array<u8, Self::Size> {
		let (a, b) = self;
		let a = a.into_bytes();
		let b = b.into_bytes();
		a.concat(b)
	}
}

pub fn merge_into_bytes<T>(value: T) -> Array<u8, T::Size>
where
	T: IntoByteArray,
{
	value.into_bytes()
}


#[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;

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

		fn into_bytes(self) -> Array<u8, Self::Size> {
			BigEndian(self.0).into_bytes()
		}
	}
	struct Bar(u32);
	impl IntoByteArray for Bar {
		type Size = U4;

		fn into_bytes(self) -> Array<u8, Self::Size> {
			BigEndian(self.0).into_bytes()
		}
	}

	#[test]
	fn test_merge_bytes_u8() {
		let input = 42_u8;
		let output = merge_into_bytes(input);
		assert_eq!(output, Array([42]));
	}

	#[test]
	fn test_merge_bytes_id() {
		let input = Array::<u8, U4>([1, 2, 3, 4]);
		let output = merge_into_bytes(input);
		assert_eq!(output, input);
	}

	#[test]
	fn test_merge_bytes_array() {
		let input = [1, 2, 3, 4_u8];
		let output = merge_into_bytes(input);
		assert_eq!(output, Array([1, 2, 3, 4]));
	}

	#[test]
	fn test_merge_bytes_array_of_arrays() {
		let input = [[1, 2], [3, 4_u8]];
		let data = array_from_core(input);
		let output = merge_into_bytes(data);
		assert_eq!(output, Array([1, 2, 3, 4]));
	}

	#[test]
	fn test_merge_bytes_tuple() {
		let input = (1_u8, 2_u8);
		let output = merge_into_bytes(input);
		assert_eq!(output, Array([1, 2]));
	}

	#[test]
	fn test_merge_bytes_custom_type() {
		let input = Foo(42);
		let output = merge_into_bytes(input);
		assert_eq!(output, Array([0, 42]));
	}

	#[test]
	fn test_merge_bytes_custom_tuple() {
		let input = (Foo(42), Bar(0x1234));
		let output = merge_into_bytes(input);
		assert_eq!(output, Array([0, 42, 0x0, 0x0, 0x12, 0x34]));
	}

	#[test]
	fn test_merge_bytes() {
		let foo = Foo(42);
		let bar = Bar(0x1234);
		let padding = 0_u8;
		let alpha = *b"abc";

		let data = merge_bytes!([foo, bar, padding, alpha]);

		assert_eq!(
			data,
			Array([
				0, 42, // foo
				0x0, 0x0, 0x12, 0x34, // bar
				0,    // padding
				b'a', b'b', b'c', // alpha
			])
		);
	}
}