use core::convert::Infallible;
use core::fmt::Debug;
use core::marker::PhantomData;
#[cfg(feature = "std")]
use thiserror::Error;
use crate::utils::infallible::IsInfallible;
use super::{
primitive::{FieldCopyAccess, FieldView},
Field, StorageIntoFieldView, StorageToFieldView,
};
pub trait LayoutAs<U>: Sized {
type ReadError;
type WriteError;
fn try_read(v: U) -> Result<Self, Self::ReadError>;
fn try_write(v: Self) -> Result<U, Self::WriteError>;
}
#[derive(Debug)]
#[cfg_attr(feature = "std", derive(Error))]
pub enum WrappedFieldError<PrimitiveAccessError, LayoutAsError> {
#[cfg_attr(
feature = "std",
error("Error accessing (reading or writing) the primitive data type: {0}")
)]
PrimitiveAccessError(PrimitiveAccessError),
#[cfg_attr(
feature = "std",
error("Error mapping the primitive data type in `LayoutAs`: {0}")
)]
LayoutAsError(LayoutAsError),
}
impl IsInfallible for WrappedFieldError<Infallible, Infallible> {}
pub struct WrappedField<U, T: LayoutAs<U>, F: Field> {
_p1: PhantomData<U>,
_p2: PhantomData<T>,
_p3: PhantomData<F>,
}
impl<U, T: LayoutAs<U>, F: Field> Field for WrappedField<U, T, F> {
type Endian = F::Endian;
const OFFSET: usize = F::OFFSET;
const SIZE: Option<usize> = F::SIZE;
}
impl<
'a,
U,
T: LayoutAs<U>,
F: FieldCopyAccess<HighLevelType = U> + StorageToFieldView<&'a [u8]>,
> StorageToFieldView<&'a [u8]> for WrappedField<U, T, F>
{
type View = FieldView<&'a [u8], Self>;
#[inline(always)]
fn view(storage: &'a [u8]) -> Self::View {
Self::View::new(storage)
}
}
impl<
'a,
U,
T: LayoutAs<U>,
F: FieldCopyAccess<HighLevelType = U> + StorageToFieldView<&'a mut [u8]>,
> StorageToFieldView<&'a mut [u8]> for WrappedField<U, T, F>
{
type View = FieldView<&'a mut [u8], Self>;
#[inline(always)]
fn view(storage: &'a mut [u8]) -> Self::View {
Self::View::new(storage)
}
}
impl<
U,
S: AsRef<[u8]>,
T: LayoutAs<U>,
F: FieldCopyAccess<HighLevelType = U> + StorageIntoFieldView<S>,
> StorageIntoFieldView<S> for WrappedField<U, T, F>
{
type View = FieldView<S, Self>;
#[inline(always)]
fn into_view(storage: S) -> Self::View {
Self::View::new(storage)
}
}
impl<U, T: LayoutAs<U>, F: FieldCopyAccess<HighLevelType = U>> FieldCopyAccess
for WrappedField<U, T, F>
{
type ReadError = WrappedFieldError<F::ReadError, T::ReadError>;
type WriteError = WrappedFieldError<F::WriteError, T::WriteError>;
type HighLevelType = T;
#[inline(always)]
fn try_read(storage: &[u8]) -> Result<Self::HighLevelType, Self::ReadError> {
let v = F::try_read(storage).map_err(WrappedFieldError::PrimitiveAccessError)?;
let value = <T as LayoutAs<U>>::try_read(v).map_err(WrappedFieldError::LayoutAsError)?;
Ok(value)
}
#[inline(always)]
fn try_write(storage: &mut [u8], v: Self::HighLevelType) -> Result<(), Self::WriteError> {
let v = <T as LayoutAs<U>>::try_write(v).map_err(WrappedFieldError::LayoutAsError)?;
F::try_write(storage, v).map_err(WrappedFieldError::PrimitiveAccessError)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::float_cmp)]
use crate::prelude::*;
use crate::{LayoutAs, PrimitiveField, WrappedField};
use core::convert::{Infallible, TryInto};
#[derive(Debug, PartialEq, Eq)]
struct Wrapped<T>(T);
impl<T> LayoutAs<T> for Wrapped<T> {
type ReadError = Infallible;
type WriteError = Infallible;
fn try_read(v: T) -> Result<Self, Infallible> {
Ok(Self(v))
}
fn try_write(v: Self) -> Result<T, Infallible> {
Ok(v.0)
}
}
macro_rules! test_wrapped_field {
($type:ty, $expected_size:expr, $value1:expr, $value2:expr) => {
test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, little, LittleEndian, from_le_bytes);
test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, big, BigEndian, from_be_bytes);
test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, native, NativeEndian, from_ne_bytes);
};
(@case, $type:ty, $expected_size:expr, $value1:expr, $value2: expr, $endian:ident, $endian_type:ty, $endian_fn:ident) => {
$crate::internal::paste! {
#[test]
fn [<test_ $type _ $endian endian>]() {
let mut storage = [0; 1024];
type Field1 = WrappedField<$type, Wrapped<$type>, PrimitiveField<$type, $endian_type, 5>>;
type Field2 = WrappedField<$type, Wrapped<$type>, PrimitiveField<$type, $endian_type, 123>>;
Field1::write(&mut storage, Wrapped($value1));
Field2::write(&mut storage, Wrapped($value2));
assert_eq!(Wrapped($value1), Field1::read(&storage));
assert_eq!(Wrapped($value2), Field2::read(&storage));
assert_eq!($value1, <$type>::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
assert_eq!(
$value2,
<$type>::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap())
);
assert_eq!(Some($expected_size), Field1::SIZE);
assert_eq!(5, Field1::OFFSET);
assert_eq!(Some($expected_size), Field2::SIZE);
assert_eq!(123, Field2::OFFSET);
}
}
};
}
test_wrapped_field!(i8, 1, 50, -20);
test_wrapped_field!(i16, 2, 500, -2000);
test_wrapped_field!(i32, 4, 10i32.pow(8), -(10i32.pow(7)));
test_wrapped_field!(i64, 8, 10i64.pow(15), -(10i64.pow(14)));
test_wrapped_field!(i128, 16, 10i128.pow(30), -(10i128.pow(28)));
test_wrapped_field!(u8, 1, 50, 20);
test_wrapped_field!(u16, 2, 500, 2000);
test_wrapped_field!(u32, 4, 10u32.pow(8), (10u32.pow(7)));
test_wrapped_field!(u64, 8, 10u64.pow(15), (10u64.pow(14)));
test_wrapped_field!(u128, 16, 10u128.pow(30), (10u128.pow(28)));
test_wrapped_field!(f32, 4, 10f32.powf(8.31), -(10f32.powf(7.31)));
test_wrapped_field!(f64, 8, 10f64.powf(15.31), -(10f64.powf(15.31)));
macro_rules! test_wrapped_unit_field {
($endian:ident, $endian_type:ty) => {
$crate::internal::paste! {
#[allow(clippy::unit_cmp)]
#[test]
fn [<test_unit_ $endian endian>]() {
let mut storage = [0; 1024];
type Field1 = WrappedField<(), Wrapped<()>, PrimitiveField<(), LittleEndian, 5>>;
type Field2 = WrappedField<(), Wrapped<()>, PrimitiveField<(), LittleEndian, 123>>;
Field1::write(&mut storage, Wrapped(()));
Field2::write(&mut storage, Wrapped(()));
assert_eq!(Wrapped(()), Field1::read(&storage));
assert_eq!(Wrapped(()), Field2::read(&storage));
assert_eq!(Some(0), Field1::SIZE);
assert_eq!(5, Field1::OFFSET);
assert_eq!(Some(0), Field2::SIZE);
assert_eq!(123, Field2::OFFSET);
assert_eq!(storage, [0; 1024]);
}
}
};
}
test_wrapped_unit_field!(little, LittleEndian);
test_wrapped_unit_field!(big, BigEndian);
test_wrapped_unit_field!(native, NativeEndian);
mod read_error_write_infallible {
use super::*;
use crate::WrappedFieldError;
#[derive(Debug)]
struct ErrorType;
const SUCCESS_VALUE: u8 = 10;
const ERROR_VALUE: u8 = 100;
#[derive(Debug, PartialEq, Eq)]
struct Wrapped(u8);
impl LayoutAs<u8> for Wrapped {
type ReadError = ErrorType;
type WriteError = Infallible;
fn try_read(v: u8) -> Result<Self, ErrorType> {
if v == ERROR_VALUE {
Err(ErrorType)
} else {
Ok(Self(v))
}
}
fn try_write(v: Self) -> Result<u8, Infallible> {
Ok(v.0)
}
}
#[test]
fn test_metadata() {
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
assert_eq!(Some(1), Field1::SIZE);
assert_eq!(5, Field1::OFFSET);
}
#[test]
fn test_success() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
Field1::write(&mut storage, Wrapped(SUCCESS_VALUE));
assert_eq!(Wrapped(SUCCESS_VALUE), Field1::try_read(&storage).unwrap());
assert_eq!(
SUCCESS_VALUE,
u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
);
}
#[test]
fn test_read_error() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
storage[5..6].copy_from_slice(&ERROR_VALUE.to_le_bytes());
assert!(matches!(
Field1::try_read(&storage),
Err(WrappedFieldError::LayoutAsError(ErrorType)),
));
assert_eq!(
ERROR_VALUE,
u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
);
}
}
mod read_infallible_write_error {
use super::*;
use crate::WrappedFieldError;
#[derive(Debug)]
struct ErrorType;
const ERROR_VALUE: u8 = 100;
const SUCCESS_VALUE: u8 = 10;
#[derive(Debug, PartialEq, Eq)]
struct Wrapped(u8);
impl LayoutAs<u8> for Wrapped {
type ReadError = Infallible;
type WriteError = ErrorType;
fn try_read(v: u8) -> Result<Self, Infallible> {
Ok(Self(v))
}
fn try_write(v: Self) -> Result<u8, ErrorType> {
if v.0 == ERROR_VALUE {
Err(ErrorType)
} else {
Ok(v.0)
}
}
}
#[test]
fn test_metadata() {
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
assert_eq!(Some(1), Field1::SIZE);
assert_eq!(5, Field1::OFFSET);
}
#[test]
fn test_success() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
Field1::try_write(&mut storage, Wrapped(SUCCESS_VALUE)).unwrap();
assert_eq!(Wrapped(SUCCESS_VALUE), Field1::read(&storage));
assert_eq!(
SUCCESS_VALUE,
u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
);
}
#[test]
fn test_write_error() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
assert!(matches!(
Field1::try_write(&mut storage, Wrapped(ERROR_VALUE)),
Err(WrappedFieldError::LayoutAsError(ErrorType)),
));
assert_eq!(0, u8::from_le_bytes((&storage[5..6]).try_into().unwrap()));
}
}
mod read_error_write_error {
use super::*;
use crate::WrappedFieldError;
#[derive(Debug)]
struct ReadErrorType;
#[derive(Debug)]
struct WriteErrorType;
const ERROR_VALUE: u8 = 100;
const SUCCESS_VALUE: u8 = 10;
#[derive(Debug, PartialEq, Eq)]
struct Wrapped(u8);
impl LayoutAs<u8> for Wrapped {
type ReadError = ReadErrorType;
type WriteError = WriteErrorType;
fn try_read(v: u8) -> Result<Self, ReadErrorType> {
if v == ERROR_VALUE {
Err(ReadErrorType)
} else {
Ok(Wrapped(v))
}
}
fn try_write(v: Self) -> Result<u8, WriteErrorType> {
if v.0 == ERROR_VALUE {
Err(WriteErrorType)
} else {
Ok(v.0)
}
}
}
#[test]
fn test_metadata() {
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
assert_eq!(Some(1), Field1::SIZE);
assert_eq!(5, Field1::OFFSET);
}
#[test]
fn test_success() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
Field1::try_write(&mut storage, Wrapped(SUCCESS_VALUE)).unwrap();
assert_eq!(Wrapped(SUCCESS_VALUE), Field1::try_read(&storage).unwrap());
assert_eq!(
SUCCESS_VALUE,
u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
);
}
#[test]
fn test_read_error() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
storage[5..6].copy_from_slice(&ERROR_VALUE.to_le_bytes());
assert!(matches!(
Field1::try_read(&storage),
Err(WrappedFieldError::LayoutAsError(ReadErrorType)),
));
assert_eq!(
ERROR_VALUE,
u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
);
}
#[test]
fn test_write_error() {
let mut storage = [0; 1024];
type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
assert!(matches!(
Field1::try_write(&mut storage, Wrapped(ERROR_VALUE)),
Err(WrappedFieldError::LayoutAsError(WriteErrorType)),
));
assert_eq!(0, u8::from_le_bytes((&storage[5..6]).try_into().unwrap()));
}
}
}