1use core::convert::Infallible;
2use core::fmt::Debug;
3use core::marker::PhantomData;
4#[cfg(feature = "std")]
5use thiserror::Error;
6
7use crate::utils::infallible::IsInfallible;
8
9use super::{
10 primitive::{FieldCopyAccess, FieldView},
11 Field, StorageIntoFieldView, StorageToFieldView,
12};
13
14pub trait LayoutAs<U>: Sized {
46 type ReadError;
52 type WriteError;
58
59 fn try_read(v: U) -> Result<Self, Self::ReadError>;
62
63 fn try_write(v: Self) -> Result<U, Self::WriteError>;
66}
67
68#[derive(Debug)]
70#[cfg_attr(feature = "std", derive(Error))]
71pub enum WrappedFieldError<PrimitiveAccessError, LayoutAsError> {
72 #[cfg_attr(
74 feature = "std",
75 error("Error accessing (reading or writing) the primitive data type: {0}")
76 )]
77 PrimitiveAccessError(PrimitiveAccessError),
78 #[cfg_attr(
80 feature = "std",
81 error("Error mapping the primitive data type in `LayoutAs`: {0}")
82 )]
83 LayoutAsError(LayoutAsError),
84}
85
86impl IsInfallible for WrappedFieldError<Infallible, Infallible> {}
87
88pub struct WrappedField<U, T: LayoutAs<U>, F: Field> {
176 _p1: PhantomData<U>,
177 _p2: PhantomData<T>,
178 _p3: PhantomData<F>,
179}
180
181impl<U, T: LayoutAs<U>, F: Field> Field for WrappedField<U, T, F> {
182 type Endian = F::Endian;
184 const OFFSET: usize = F::OFFSET;
186 const SIZE: Option<usize> = F::SIZE;
188}
189
190impl<
191 'a,
192 U,
193 T: LayoutAs<U>,
194 F: FieldCopyAccess<HighLevelType = U> + StorageToFieldView<&'a [u8]>,
195 > StorageToFieldView<&'a [u8]> for WrappedField<U, T, F>
196{
197 type View = FieldView<&'a [u8], Self>;
198
199 #[inline(always)]
200 fn view(storage: &'a [u8]) -> Self::View {
201 Self::View::new(storage)
202 }
203}
204
205impl<
206 'a,
207 U,
208 T: LayoutAs<U>,
209 F: FieldCopyAccess<HighLevelType = U> + StorageToFieldView<&'a mut [u8]>,
210 > StorageToFieldView<&'a mut [u8]> for WrappedField<U, T, F>
211{
212 type View = FieldView<&'a mut [u8], Self>;
213
214 #[inline(always)]
215 fn view(storage: &'a mut [u8]) -> Self::View {
216 Self::View::new(storage)
217 }
218}
219
220impl<
221 U,
222 S: AsRef<[u8]>,
223 T: LayoutAs<U>,
224 F: FieldCopyAccess<HighLevelType = U> + StorageIntoFieldView<S>,
225 > StorageIntoFieldView<S> for WrappedField<U, T, F>
226{
227 type View = FieldView<S, Self>;
228
229 #[inline(always)]
230 fn into_view(storage: S) -> Self::View {
231 Self::View::new(storage)
232 }
233}
234
235impl<U, T: LayoutAs<U>, F: FieldCopyAccess<HighLevelType = U>> FieldCopyAccess
236 for WrappedField<U, T, F>
237{
238 type ReadError = WrappedFieldError<F::ReadError, T::ReadError>;
240 type WriteError = WrappedFieldError<F::WriteError, T::WriteError>;
242 type HighLevelType = T;
244
245 #[inline(always)]
284 fn try_read(storage: &[u8]) -> Result<Self::HighLevelType, Self::ReadError> {
285 let v = F::try_read(storage).map_err(WrappedFieldError::PrimitiveAccessError)?;
286 let value = <T as LayoutAs<U>>::try_read(v).map_err(WrappedFieldError::LayoutAsError)?;
287 Ok(value)
288 }
289
290 #[inline(always)]
295 fn try_write(storage: &mut [u8], v: Self::HighLevelType) -> Result<(), Self::WriteError> {
296 let v = <T as LayoutAs<U>>::try_write(v).map_err(WrappedFieldError::LayoutAsError)?;
297 F::try_write(storage, v).map_err(WrappedFieldError::PrimitiveAccessError)?;
298 Ok(())
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 #![allow(clippy::float_cmp)]
305 use crate::prelude::*;
306 use crate::{LayoutAs, PrimitiveField, WrappedField};
307 use core::convert::{Infallible, TryInto};
308
309 #[derive(Debug, PartialEq, Eq)]
310 struct Wrapped<T>(T);
311 impl<T> LayoutAs<T> for Wrapped<T> {
312 type ReadError = Infallible;
313 type WriteError = Infallible;
314
315 fn try_read(v: T) -> Result<Self, Infallible> {
316 Ok(Self(v))
317 }
318 fn try_write(v: Self) -> Result<T, Infallible> {
319 Ok(v.0)
320 }
321 }
322
323 macro_rules! test_wrapped_field {
324 ($type:ty, $expected_size:expr, $value1:expr, $value2:expr) => {
325 test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, little, LittleEndian, from_le_bytes);
326 test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, big, BigEndian, from_be_bytes);
327 test_wrapped_field!(@case, $type, $expected_size, $value1, $value2, native, NativeEndian, from_ne_bytes);
328 };
329 (@case, $type:ty, $expected_size:expr, $value1:expr, $value2: expr, $endian:ident, $endian_type:ty, $endian_fn:ident) => {
330 $crate::internal::paste! {
331 #[test]
332 fn [<test_ $type _ $endian endian>]() {
333 let mut storage = [0; 1024];
334
335 type Field1 = WrappedField<$type, Wrapped<$type>, PrimitiveField<$type, $endian_type, 5>>;
336 type Field2 = WrappedField<$type, Wrapped<$type>, PrimitiveField<$type, $endian_type, 123>>;
337
338 Field1::write(&mut storage, Wrapped($value1));
339 Field2::write(&mut storage, Wrapped($value2));
340
341 assert_eq!(Wrapped($value1), Field1::read(&storage));
342 assert_eq!(Wrapped($value2), Field2::read(&storage));
343
344 assert_eq!($value1, <$type>::$endian_fn((&storage[5..(5+$expected_size)]).try_into().unwrap()));
345 assert_eq!(
346 $value2,
347 <$type>::$endian_fn((&storage[123..(123+$expected_size)]).try_into().unwrap())
348 );
349
350 assert_eq!(Some($expected_size), Field1::SIZE);
351 assert_eq!(5, Field1::OFFSET);
352 assert_eq!(Some($expected_size), Field2::SIZE);
353 assert_eq!(123, Field2::OFFSET);
354 }
355 }
356 };
357 }
358
359 test_wrapped_field!(i8, 1, 50, -20);
360 test_wrapped_field!(i16, 2, 500, -2000);
361 test_wrapped_field!(i32, 4, 10i32.pow(8), -(10i32.pow(7)));
362 test_wrapped_field!(i64, 8, 10i64.pow(15), -(10i64.pow(14)));
363 test_wrapped_field!(i128, 16, 10i128.pow(30), -(10i128.pow(28)));
364
365 test_wrapped_field!(u8, 1, 50, 20);
366 test_wrapped_field!(u16, 2, 500, 2000);
367 test_wrapped_field!(u32, 4, 10u32.pow(8), (10u32.pow(7)));
368 test_wrapped_field!(u64, 8, 10u64.pow(15), (10u64.pow(14)));
369 test_wrapped_field!(u128, 16, 10u128.pow(30), (10u128.pow(28)));
370
371 test_wrapped_field!(f32, 4, 10f32.powf(8.31), -(10f32.powf(7.31)));
372 test_wrapped_field!(f64, 8, 10f64.powf(15.31), -(10f64.powf(15.31)));
373
374 macro_rules! test_wrapped_unit_field {
375 ($endian:ident, $endian_type:ty) => {
376 $crate::internal::paste! {
377 #[allow(clippy::unit_cmp)]
378 #[test]
379 fn [<test_unit_ $endian endian>]() {
380 let mut storage = [0; 1024];
381
382 type Field1 = WrappedField<(), Wrapped<()>, PrimitiveField<(), LittleEndian, 5>>;
383 type Field2 = WrappedField<(), Wrapped<()>, PrimitiveField<(), LittleEndian, 123>>;
384
385 Field1::write(&mut storage, Wrapped(()));
386 Field2::write(&mut storage, Wrapped(()));
387
388 assert_eq!(Wrapped(()), Field1::read(&storage));
389 assert_eq!(Wrapped(()), Field2::read(&storage));
390
391 assert_eq!(Some(0), Field1::SIZE);
392 assert_eq!(5, Field1::OFFSET);
393 assert_eq!(Some(0), Field2::SIZE);
394 assert_eq!(123, Field2::OFFSET);
395
396 assert_eq!(storage, [0; 1024]);
399 }
400 }
401 };
402 }
403
404 test_wrapped_unit_field!(little, LittleEndian);
405 test_wrapped_unit_field!(big, BigEndian);
406 test_wrapped_unit_field!(native, NativeEndian);
407
408 mod read_error_write_infallible {
409 use super::*;
410 use crate::WrappedFieldError;
411
412 #[derive(Debug)]
413 struct ErrorType;
414
415 const SUCCESS_VALUE: u8 = 10;
416 const ERROR_VALUE: u8 = 100;
417
418 #[derive(Debug, PartialEq, Eq)]
419 struct Wrapped(u8);
420 impl LayoutAs<u8> for Wrapped {
421 type ReadError = ErrorType;
422 type WriteError = Infallible;
423
424 fn try_read(v: u8) -> Result<Self, ErrorType> {
425 if v == ERROR_VALUE {
426 Err(ErrorType)
427 } else {
428 Ok(Self(v))
429 }
430 }
431 fn try_write(v: Self) -> Result<u8, Infallible> {
432 Ok(v.0)
433 }
434 }
435
436 #[test]
437 fn test_metadata() {
438 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
439 assert_eq!(Some(1), Field1::SIZE);
440 assert_eq!(5, Field1::OFFSET);
441 }
442
443 #[test]
444 fn test_success() {
445 let mut storage = [0; 1024];
446 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
447
448 Field1::write(&mut storage, Wrapped(SUCCESS_VALUE));
449 assert_eq!(Wrapped(SUCCESS_VALUE), Field1::try_read(&storage).unwrap());
450
451 assert_eq!(
452 SUCCESS_VALUE,
453 u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
454 );
455 }
456
457 #[test]
458 fn test_read_error() {
459 let mut storage = [0; 1024];
460 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
461
462 storage[5..6].copy_from_slice(&ERROR_VALUE.to_le_bytes());
463 assert!(matches!(
464 Field1::try_read(&storage),
465 Err(WrappedFieldError::LayoutAsError(ErrorType)),
466 ));
467
468 assert_eq!(
469 ERROR_VALUE,
470 u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
471 );
472 }
473 }
474
475 mod read_infallible_write_error {
476 use super::*;
477 use crate::WrappedFieldError;
478
479 #[derive(Debug)]
480 struct ErrorType;
481 const ERROR_VALUE: u8 = 100;
482 const SUCCESS_VALUE: u8 = 10;
483
484 #[derive(Debug, PartialEq, Eq)]
485 struct Wrapped(u8);
486 impl LayoutAs<u8> for Wrapped {
487 type ReadError = Infallible;
488 type WriteError = ErrorType;
489
490 fn try_read(v: u8) -> Result<Self, Infallible> {
491 Ok(Self(v))
492 }
493 fn try_write(v: Self) -> Result<u8, ErrorType> {
494 if v.0 == ERROR_VALUE {
495 Err(ErrorType)
496 } else {
497 Ok(v.0)
498 }
499 }
500 }
501
502 #[test]
503 fn test_metadata() {
504 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
505 assert_eq!(Some(1), Field1::SIZE);
506 assert_eq!(5, Field1::OFFSET);
507 }
508
509 #[test]
510 fn test_success() {
511 let mut storage = [0; 1024];
512 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
513
514 Field1::try_write(&mut storage, Wrapped(SUCCESS_VALUE)).unwrap();
515 assert_eq!(Wrapped(SUCCESS_VALUE), Field1::read(&storage));
516
517 assert_eq!(
518 SUCCESS_VALUE,
519 u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
520 );
521 }
522
523 #[test]
524 fn test_write_error() {
525 let mut storage = [0; 1024];
526 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
527
528 assert!(matches!(
529 Field1::try_write(&mut storage, Wrapped(ERROR_VALUE)),
530 Err(WrappedFieldError::LayoutAsError(ErrorType)),
531 ));
532
533 assert_eq!(0, u8::from_le_bytes((&storage[5..6]).try_into().unwrap()));
534 }
535 }
536
537 mod read_error_write_error {
538 use super::*;
539 use crate::WrappedFieldError;
540
541 #[derive(Debug)]
542 struct ReadErrorType;
543 #[derive(Debug)]
544 struct WriteErrorType;
545 const ERROR_VALUE: u8 = 100;
546 const SUCCESS_VALUE: u8 = 10;
547
548 #[derive(Debug, PartialEq, Eq)]
549 struct Wrapped(u8);
550 impl LayoutAs<u8> for Wrapped {
551 type ReadError = ReadErrorType;
552 type WriteError = WriteErrorType;
553
554 fn try_read(v: u8) -> Result<Self, ReadErrorType> {
555 if v == ERROR_VALUE {
556 Err(ReadErrorType)
557 } else {
558 Ok(Wrapped(v))
559 }
560 }
561 fn try_write(v: Self) -> Result<u8, WriteErrorType> {
562 if v.0 == ERROR_VALUE {
563 Err(WriteErrorType)
564 } else {
565 Ok(v.0)
566 }
567 }
568 }
569
570 #[test]
571 fn test_metadata() {
572 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
573 assert_eq!(Some(1), Field1::SIZE);
574 assert_eq!(5, Field1::OFFSET);
575 }
576
577 #[test]
578 fn test_success() {
579 let mut storage = [0; 1024];
580 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
581
582 Field1::try_write(&mut storage, Wrapped(SUCCESS_VALUE)).unwrap();
583 assert_eq!(Wrapped(SUCCESS_VALUE), Field1::try_read(&storage).unwrap());
584
585 assert_eq!(
586 SUCCESS_VALUE,
587 u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
588 );
589 }
590
591 #[test]
592 fn test_read_error() {
593 let mut storage = [0; 1024];
594 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
595
596 storage[5..6].copy_from_slice(&ERROR_VALUE.to_le_bytes());
597 assert!(matches!(
598 Field1::try_read(&storage),
599 Err(WrappedFieldError::LayoutAsError(ReadErrorType)),
600 ));
601
602 assert_eq!(
603 ERROR_VALUE,
604 u8::from_le_bytes((&storage[5..6]).try_into().unwrap())
605 );
606 }
607
608 #[test]
609 fn test_write_error() {
610 let mut storage = [0; 1024];
611 type Field1 = WrappedField<u8, Wrapped, PrimitiveField<u8, LittleEndian, 5>>;
612
613 assert!(matches!(
614 Field1::try_write(&mut storage, Wrapped(ERROR_VALUE)),
615 Err(WrappedFieldError::LayoutAsError(WriteErrorType)),
616 ));
617
618 assert_eq!(0, u8::from_le_bytes((&storage[5..6]).try_into().unwrap()));
619 }
620 }
621}