diff --git a/src/util.rs b/src/util.rs index ee599a3..9705c5f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,7 +1,9 @@ use crate::{ByteConversionError, SizeMissmatch}; -use core::fmt::Debug; +use core::fmt::{Debug, Display, Formatter}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] +use std::error::Error; pub trait ToBeBytes { type ByteArray: AsRef<[u8]>; @@ -74,6 +76,43 @@ pub trait UnsignedEnum { pub trait UnsignedEnumExt: UnsignedEnum + Debug + Copy + Clone + PartialEq + Eq {} +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub enum UnsignedByteFieldError { + /// Value is too large for specified width of byte field. The first value contains the width, + /// the second value contains the detected value. + ValueTooLargeForWidth((usize, u64)), + /// Only 1, 2, 4 and 8 are allow width values. Optionally contains the expected width if + /// applicable, for example for conversions. + InvalidWidth(usize, Option), + ByteConversionError(ByteConversionError), +} + +impl From for UnsignedByteFieldError { + fn from(value: ByteConversionError) -> Self { + Self::ByteConversionError(value) + } +} + +impl Display for UnsignedByteFieldError { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + match self { + Self::ByteConversionError(e) => { + write!(f, "low level byte conversion error: {e}") + } + Self::InvalidWidth(val, _) => { + write!(f, "invalid width {val}, only 1, 2, 4 and 8 are allowed.") + } + Self::ValueTooLargeForWidth((width, value)) => { + write!(f, "value {value} too large for width {width}") + } + } + } +} + +#[cfg(feature = "std")] +impl Error for UnsignedByteFieldError {} + /// Type erased variant. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -87,30 +126,30 @@ impl UnsignedByteField { Self { width, value } } - pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result { + pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result { if width > buf.len() { return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { found: buf.len(), expected: width, - })); + }) + .into()); } match width { - 0 => Ok(Self::new(0, 0)), - 1 => Ok(Self::new(1, buf[0] as u64)), + 0 => Ok(Self::new(width, 0)), + 1 => Ok(Self::new(width, buf[0] as u64)), 2 => Ok(Self::new( - 2, + width, u16::from_be_bytes(buf[0..2].try_into().unwrap()) as u64, )), 4 => Ok(Self::new( - 2, + width, u32::from_be_bytes(buf[0..4].try_into().unwrap()) as u64, )), 8 => Ok(Self::new( - 2, + width, u64::from_be_bytes(buf[0..8].try_into().unwrap()), )), - // TODO: I don't know whether it is a good idea to panic here. - _ => panic!("invalid width"), + _ => Err(UnsignedByteFieldError::InvalidWidth(width, None)), } } } @@ -156,18 +195,18 @@ impl UnsignedEnum for UnsignedByteField { #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct GenericUnsignedByteField { - val: TYPE, + value: TYPE, } impl GenericUnsignedByteField { pub fn new(val: TYPE) -> Self { - Self { val } + Self { value: val } } } impl UnsignedEnum for GenericUnsignedByteField { fn len(&self) -> usize { - self.val.written_len() + self.value.written_len() } fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { @@ -177,7 +216,7 @@ impl UnsignedEnum for GenericUnsignedByteField { expected: self.len(), })); } - buf[0..self.len()].copy_from_slice(self.val.to_be_bytes().as_ref()); + buf[0..self.len()].copy_from_slice(self.value.to_be_bytes().as_ref()); Ok(()) } } @@ -190,16 +229,16 @@ pub type UnsignedU64 = GenericUnsignedByteField; impl From for UnsignedByteField { fn from(value: UnsignedU8) -> Self { - Self::new(1, value.val as u64) + Self::new(1, value.value as u64) } } impl TryFrom for UnsignedU8 { - type Error = (); + type Error = UnsignedByteFieldError; - fn try_from(value: UnsignedByteField) -> Result { - if value.value > 2_u64.pow(8) - 1 { - return Err(()); + fn try_from(value: UnsignedByteField) -> Result { + if value.width != 1 { + return Err(UnsignedByteFieldError::InvalidWidth(value.width, Some(1))); } Ok(Self::new(value.value as u8)) } @@ -207,16 +246,16 @@ impl TryFrom for UnsignedU8 { impl From for UnsignedByteField { fn from(value: UnsignedU16) -> Self { - Self::new(2, value.val as u64) + Self::new(2, value.value as u64) } } impl TryFrom for UnsignedU16 { - type Error = (); + type Error = UnsignedByteFieldError; - fn try_from(value: UnsignedByteField) -> Result { - if value.value > 2_u64.pow(16) - 1 { - return Err(()); + fn try_from(value: UnsignedByteField) -> Result { + if value.width != 2 { + return Err(UnsignedByteFieldError::InvalidWidth(value.width, Some(2))); } Ok(Self::new(value.value as u16)) } @@ -224,16 +263,16 @@ impl TryFrom for UnsignedU16 { impl From for UnsignedByteField { fn from(value: UnsignedU32) -> Self { - Self::new(4, value.val as u64) + Self::new(4, value.value as u64) } } impl TryFrom for UnsignedU32 { - type Error = (); + type Error = UnsignedByteFieldError; - fn try_from(value: UnsignedByteField) -> Result { - if value.value > 2_u64.pow(32) - 1 { - return Err(()); + fn try_from(value: UnsignedByteField) -> Result { + if value.width != 4 { + return Err(UnsignedByteFieldError::InvalidWidth(value.width, Some(4))); } Ok(Self::new(value.value as u32)) } @@ -241,17 +280,277 @@ impl TryFrom for UnsignedU32 { impl From for UnsignedByteField { fn from(value: UnsignedU64) -> Self { - Self::new(8, value.val) + Self::new(8, value.value) } } impl TryFrom for UnsignedU64 { - type Error = (); + type Error = UnsignedByteFieldError; - fn try_from(value: UnsignedByteField) -> Result { - if value.value > 2_u64.pow(64) - 1 { - return Err(()); + fn try_from(value: UnsignedByteField) -> Result { + if value.width != 8 { + return Err(UnsignedByteFieldError::InvalidWidth(value.width, Some(8))); } Ok(Self::new(value.value)) } } + +#[cfg(test)] +pub mod tests { + use crate::util::{ + UnsignedByteField, UnsignedByteFieldError, UnsignedEnum, UnsignedU16, UnsignedU32, + UnsignedU64, UnsignedU8, + }; + use std::format; + + #[test] + fn test_simple_u8() { + let u8 = UnsignedU8::new(5); + assert_eq!(u8.len(), 1); + let mut buf: [u8; 8] = [0; 8]; + u8.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + assert_eq!(buf[0], 5); + for i in 1..8 { + assert_eq!(buf[i], 0); + } + } + + #[test] + fn test_simple_u16() { + let u16 = UnsignedU16::new(3823); + assert_eq!(u16.len(), 2); + let mut buf: [u8; 8] = [0; 8]; + u16.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + let raw_val = u16::from_be_bytes(buf[0..2].try_into().unwrap()); + assert_eq!(raw_val, 3823); + for i in 2..8 { + assert_eq!(buf[i], 0); + } + } + + #[test] + fn test_simple_u32() { + let u32 = UnsignedU32::new(80932); + assert_eq!(u32.len(), 4); + let mut buf: [u8; 8] = [0; 8]; + u32.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + let raw_val = u32::from_be_bytes(buf[0..4].try_into().unwrap()); + assert_eq!(raw_val, 80932); + for i in 4..8 { + assert_eq!(buf[i], 0); + } + } + + #[test] + fn test_simple_u64() { + let u64 = UnsignedU64::new(5999999); + assert_eq!(u64.len(), 8); + let mut buf: [u8; 8] = [0; 8]; + u64.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + let raw_val = u64::from_be_bytes(buf[0..8].try_into().unwrap()); + assert_eq!(raw_val, 5999999); + } + + #[test] + fn conversions_u8() { + let u8 = UnsignedU8::new(5); + let u8_type_erased = UnsignedByteField::from(u8); + assert_eq!(u8_type_erased.width, 1); + assert_eq!(u8_type_erased.value, 5); + let u8_conv_back = UnsignedU8::try_from(u8_type_erased).expect("conversion failed for u8"); + assert_eq!(u8, u8_conv_back); + assert_eq!(u8_conv_back.value, 5); + } + + #[test] + fn conversion_u8_fails() { + let field = UnsignedByteField::new(2, 60000); + let conv_fails = UnsignedU8::try_from(field); + assert!(conv_fails.is_err()); + let err = conv_fails.unwrap_err(); + match err { + UnsignedByteFieldError::InvalidWidth(width, Some(expected)) => { + assert_eq!(width, 2); + assert_eq!(expected, 1); + } + _ => { + panic!("{}", format!("invalid error {err}")) + } + } + } + + #[test] + fn conversions_u16() { + let u16 = UnsignedU16::new(64444); + let u16_type_erased = UnsignedByteField::from(u16); + assert_eq!(u16_type_erased.width, 2); + assert_eq!(u16_type_erased.value, 64444); + let u16_conv_back = + UnsignedU16::try_from(u16_type_erased).expect("conversion failed for u16"); + assert_eq!(u16, u16_conv_back); + assert_eq!(u16_conv_back.value, 64444); + } + + #[test] + fn conversion_u16_fails() { + let field = UnsignedByteField::new(4, 75000); + let conv_fails = UnsignedU16::try_from(field); + assert!(conv_fails.is_err()); + let err = conv_fails.unwrap_err(); + match err { + UnsignedByteFieldError::InvalidWidth(width, Some(expected)) => { + assert_eq!(width, 4); + assert_eq!(expected, 2); + } + _ => { + panic!("{}", format!("invalid error {err}")) + } + } + } + + #[test] + fn conversions_u32() { + let u32 = UnsignedU32::new(75000); + let u32_type_erased = UnsignedByteField::from(u32); + assert_eq!(u32_type_erased.width, 4); + assert_eq!(u32_type_erased.value, 75000); + let u32_conv_back = + UnsignedU32::try_from(u32_type_erased).expect("conversion failed for u32"); + assert_eq!(u32, u32_conv_back); + assert_eq!(u32_conv_back.value, 75000); + } + + #[test] + fn conversion_u32_fails() { + let field = UnsignedByteField::new(8, 75000); + let conv_fails = UnsignedU32::try_from(field); + assert!(conv_fails.is_err()); + let err = conv_fails.unwrap_err(); + match err { + UnsignedByteFieldError::InvalidWidth(width, Some(expected)) => { + assert_eq!(width, 8); + assert_eq!(expected, 4); + } + _ => { + panic!("{}", format!("invalid error {err}")) + } + } + } + + #[test] + fn conversions_u64() { + let u64 = UnsignedU64::new(5999999); + let u64_type_erased = UnsignedByteField::from(u64); + assert_eq!(u64_type_erased.width, 8); + assert_eq!(u64_type_erased.value, 5999999); + let u64_conv_back = + UnsignedU64::try_from(u64_type_erased).expect("conversion failed for u64"); + assert_eq!(u64, u64_conv_back); + assert_eq!(u64_conv_back.value, 5999999); + } + + #[test] + fn conversion_u64_fails() { + let field = UnsignedByteField::new(4, 60000); + let conv_fails = UnsignedU64::try_from(field); + assert!(conv_fails.is_err()); + let err = conv_fails.unwrap_err(); + match err { + UnsignedByteFieldError::InvalidWidth(width, Some(expected)) => { + assert_eq!(width, 4); + assert_eq!(expected, 8); + } + _ => { + panic!("{}", format!("invalid error {err}")) + } + } + } + + #[test] + fn type_erased_u8_write() { + let u8 = UnsignedByteField::new(1, 5); + assert_eq!(u8.len(), 1); + let mut buf: [u8; 8] = [0; 8]; + u8.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + assert_eq!(buf[0], 5); + for i in 1..8 { + assert_eq!(buf[i], 0); + } + } + + #[test] + fn type_erased_u16_write() { + let u16 = UnsignedByteField::new(2, 3823); + assert_eq!(u16.len(), 2); + let mut buf: [u8; 8] = [0; 8]; + u16.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + let raw_val = u16::from_be_bytes(buf[0..2].try_into().unwrap()); + assert_eq!(raw_val, 3823); + for i in 2..8 { + assert_eq!(buf[i], 0); + } + } + + #[test] + fn type_erased_u32_write() { + let u32 = UnsignedByteField::new(4, 80932); + assert_eq!(u32.len(), 4); + let mut buf: [u8; 8] = [0; 8]; + u32.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + let raw_val = u32::from_be_bytes(buf[0..4].try_into().unwrap()); + assert_eq!(raw_val, 80932); + for i in 4..8 { + assert_eq!(buf[i], 0); + } + } + + #[test] + fn type_erased_u64_write() { + let u64 = UnsignedByteField::new(8, 5999999); + assert_eq!(u64.len(), 8); + let mut buf: [u8; 8] = [0; 8]; + u64.write_to_be_bytes(&mut buf) + .expect("writing to raw buffer failed"); + let raw_val = u64::from_be_bytes(buf[0..8].try_into().unwrap()); + assert_eq!(raw_val, 5999999); + } + + #[test] + fn type_erased_u8_construction() { + let buf: [u8; 2] = [5, 10]; + let u8 = UnsignedByteField::new_from_be_bytes(1, &buf).expect("construction failed"); + assert_eq!(u8.width, 1); + assert_eq!(u8.value, 5); + } + + #[test] + fn type_erased_u16_construction() { + let buf: [u8; 2] = [0x10, 0x15]; + let u16 = UnsignedByteField::new_from_be_bytes(2, &buf).expect("construction failed"); + assert_eq!(u16.width, 2); + assert_eq!(u16.value, 0x1015); + } + + #[test] + fn type_erased_u32_construction() { + let buf: [u8; 4] = [0x01, 0x02, 0x03, 0x04]; + let u32 = UnsignedByteField::new_from_be_bytes(4, &buf).expect("construction failed"); + assert_eq!(u32.width, 4); + assert_eq!(u32.value, 0x01020304); + } + + #[test] + fn type_erased_u64_construction() { + let buf: [u8; 8] = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; + let u64 = UnsignedByteField::new_from_be_bytes(8, &buf).expect("construction failed"); + assert_eq!(u64.width, 8); + assert_eq!(u64.value, 0x0102030405060708); + } +}