//! An array of specified length `N` of u8 values from 0 to `B`-1 //! //! See [`ArrayOfBase`] use crate::ArrayOfHex; use crate::Error; #[cfg(feature = "serde")] use serde::{ ser::{Serialize, Serializer}, Deserialize, }; /// An array of specified length `N` of u8 values from 0 to `B`-1 /// /// # Example - Success /// ``` /// # use array_of_base::ArrayOfBase; /// // GIVEN /// let array = [0, 1, 2, 3, 4, 0, 1, 2, 3, 4]; /// /// // WHEN /// let actual = ArrayOfBase::<10, 5>::try_new(array); /// /// // THEN /// assert!(actual.is_ok()) /// ``` /// /// # Example - Error /// /// ``` /// # use array_of_base::ArrayOfBase; /// // GIVEN /// let array = [16]; /// /// // WHEN /// let actual = ArrayOfBase::<1, 16>::try_new(array); /// /// // THEN /// assert!(actual.is_err()) /// ``` // #[wasm_bindgen::prelude::wasm_bindgen] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct ArrayOfBase { value: [u8; N], } impl ArrayOfBase { /// Create from Array of u8 numbers of the same size /// /// See [`ArrayOfBase`] for Example /// /// # Errors /// /// Will error if: /// /// - Invalid value in supplied array [`Error::InvalidValue`] /// /// # Example /// /// ``` /// # use array_of_base::{Error, ArrayOfBase}; /// // GIVEN /// let valid_array = [0u8, 1, 2, 3, 0]; /// let invalid_array = [0u8, 1, 2, 3, 4]; /// /// // WHEN /// # let actual: Vec> = /// # [valid_array, invalid_array] /// # .into_iter() /// # .map(|v| { /// ArrayOfBase::<5, 4>::try_new(v).map(|b| b.unwrap()) /// // Ran on each /// # }) /// # .collect(); /// /// // THEN /// assert_eq!( /// actual, /// vec![ /// Ok([0u8, 1, 2, 3, 0]), // valid_array /// Err(Error::InvalidValue), // invalid_array /// ] /// ); /// ``` pub fn try_new(v: [u8; N]) -> Result { if v.iter().copied().all(Self::valid_number) { Ok(Self { value: v }) } else { Err(Error::InvalidValue) } } /// Create from Vec of u8 numbers /// /// # Errors /// /// Will error if: /// /// - Too many elements in the supplied vector [`Error::Oversized`] /// - Too few, but not 0, elements in the supplied vector [`Error::Undersized`] /// - Empty vector supplied [`Error::Empty`] /// - Invalid value in supplied array [`Error::InvalidValue`] /// /// # Example /// /// ``` /// # use array_of_base::{Error, ArrayOfBase}; /// // GIVEN /// let valid_vec: Vec = vec![0, 0, 1, 2, 3]; /// let undersized_vec: Vec = vec![1]; /// let empty_vec: Vec = Vec::new(); /// let oversized_vec: Vec = vec![1, 2, 3, 1, 2, 3]; /// /// // WHEN /// # let actual: Vec> = /// # [valid_vec, undersized_vec, empty_vec, oversized_vec] /// # .iter() /// # .map(|v| { /// ArrayOfBase::<5, 4>::try_from_vec_exact(v).map(|b| b.unwrap()) /// // Ran on each /// # }) /// # .collect(); /// /// // THEN /// assert_eq!( /// actual, /// vec![ /// Ok([0u8, 0, 1, 2, 3]), // valid_vec /// Err(Error::Undersized), // undersized_vec /// Err(Error::Empty), // empty_vec /// Err(Error::Oversized), // oversized_vec /// ] /// ); /// ``` pub fn try_from_vec_exact(v: &[u8]) -> Result { use std::cmp::Ordering::{Equal, Greater, Less}; match v.len().cmp(&N) { Greater => Err(Error::Oversized), Less if v.is_empty() => Err(Error::Empty), Less => Err(Error::Undersized), Equal => Self::try_new(v.try_into().expect( "1666898415 - Unreachable: Correct length already verified", )), } } /// Create from non-empty Vec of u8 numbers, pad `0`s to the left /// /// # Errors /// /// Will error if: /// /// - Too many elements in the supplied vector [`Error::Oversized`] /// - Empty vector supplied [`Error::Empty`] /// - Invalid value in supplied array [`Error::InvalidValue`] /// /// # Example /// /// ``` /// # use array_of_base::{Error, ArrayOfBase}; /// // GIVEN /// let valid_vec: Vec = vec![1, 2, 3]; /// let empty_vec: Vec = Vec::new(); /// let oversized_vec: Vec = vec![1, 2, 3, 1, 2, 3]; /// /// // WHEN /// # let actual: Vec> = /// # [valid_vec, empty_vec, oversized_vec] /// # .iter() /// # .map(|v| { /// ArrayOfBase::<5, 4>::try_from_vec_pad(v).map(|b| b.unwrap()) /// // Ran on each /// # }) /// # .collect(); /// /// // THEN /// assert_eq!( /// actual, /// vec![ /// Ok([0u8, 0, 1, 2, 3]), // valid_vec /// Err(Error::Empty), // empty_vec /// Err(Error::Oversized), // oversized_vec /// ] /// ); /// ``` pub fn try_from_vec_pad(v: &[u8]) -> Result { use std::cmp::Ordering::{Greater, Less}; match v.len().cmp(&N) { Greater => Err(Error::Oversized), Less if v.is_empty() => Err(Error::Empty), _ => Self::try_new(Self::pad_array(v)), } } /// Return the contained verified array /// /// # Example /// /// ``` /// # use array_of_base::ArrayOfBase; /// // GIVEN /// # let valid_u8_array = [1, 2, 3]; /// # let mut verified = ArrayOfBase::<3, 10>::default(); /// verified = ArrayOfBase::try_new(valid_u8_array).unwrap(); /// /// // WHEN /// let actual = verified.unwrap(); /// /// // THEN /// assert_eq!(actual, valid_u8_array) /// ``` #[must_use] pub fn unwrap(self) -> [u8; N] { self.value } /// Borrow the contained verified array /// /// # Example /// /// ``` /// # use array_of_base::ArrayOfBase; /// // GIVEN /// # const VALID_U8_ARRAY: [u8; 3] = [1, 2, 3]; /// # let mut verified = ArrayOfBase::<3, 10>::default(); /// verified = ArrayOfBase::try_new(VALID_U8_ARRAY).unwrap(); /// /// // WHEN /// let actual = verified.borrow_u8_array(); /// /// // THEN /// assert_eq!(actual, &VALID_U8_ARRAY) /// ``` #[must_use] pub fn borrow_u8_array(&self) -> &[u8; N] { &self.value } fn valid_number(u8: u8) -> bool { u8 < B } /// Prepend to array 0 values until `N` size reached /// /// # Panics /// /// Will Panic if an array that exceeds the desired length is passed /// /// # Example /// /// ``` /// # use array_of_base::ArrayOfBase; /// // GIVEN /// let x = [1, 2, 3]; /// /// // WHEN /// let actual = ArrayOfBase::<5, 10>::pad_array(&x); /// /// // THEN /// assert_eq!(actual, [0u8, 0, 1, 2, 3]); /// ``` #[must_use] pub fn pad_array(v: &[u8]) -> [u8; N] { assert!(v.len() <= N); let mut vec = v.to_vec(); while vec.len() < N { vec.insert(0, 0); } vec.try_into().expect("1666979421 - Unreachable") } /// Reduce the size of the array to the specified value /// /// # Panics /// /// If the specified size `L` is larger than the current array size `N` the /// function will panic /// /// # Example /// ``` /// # use array_of_base::ArrayOfBase; /// // GIVEN /// let long_array = /// ArrayOfBase::<5, 4>::try_new([0u8, 1, 2, 3, 0]).unwrap(); /// /// // WHEN /// let actual: ArrayOfBase<3, 4> = long_array.trim(); /// /// // THEN /// assert_eq!(actual.borrow_u8_array(), &[0u8, 1, 2]); /// ``` #[must_use] pub fn trim(&self) -> ArrayOfBase { assert!(L <= N); ArrayOfBase { value: array_from_iter_unchecked(self.value.into_iter().take(L)), } } } impl Default for ArrayOfBase { fn default() -> Self { Self { value: [0u8; N] } } } impl std::fmt::Display for ArrayOfBase { /// Output the contents of [`ArrayOfHex`] as a hexadecimal string /// /// # Restrictions /// /// This function only works if: /// /// - The base `B` of the [`ArrayOfBase`] is `16` or [`ArrayOfHex`] is used /// /// # Example /// ``` /// # use array_of_base::ArrayOfHex; /// # const ALL_HEX_VALUES: fn() -> ArrayOfHex<16> = || { /// # ArrayOfHex::try_new(core::array::from_fn(|i| i as u8)).unwrap() /// # }; /// # /// // GIVEN /// let value: ArrayOfHex<16> = ALL_HEX_VALUES(); /// /// // WHEN /// let actual: String = value.to_string(); /// /// // THEN /// assert_eq!(actual, "0123456789abcdef"); /// ``` fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for v in self.borrow_u8_array().iter() { match v { 10 => write!(f, "a")?, 11 => write!(f, "b")?, 12 => write!(f, "c")?, 13 => write!(f, "d")?, 14 => write!(f, "e")?, 15 => write!(f, "f")?, v => write!(f, "{v}")?, } } Ok(()) } } #[cfg(feature = "serde")] impl Serialize for ArrayOfBase { /// Allow using [`ArrayOfHex`] in structs that derive [`serde::Serialize`] /// /// # Restrictions /// /// This function only works if: /// /// - The crate feature `serde` is activated /// - The base `B` of the [`ArrayOfBase`] is `16` or [`ArrayOfHex`] is used /// /// # Example /// /// ``` /// # use array_of_base::{ArrayOfHex}; /// # /// # #[derive(serde::Serialize)] /// # struct DummyStruct { /// # hex_value: ArrayOfHex<5>, /// # } /// # /// // GIVEN /// let example_struct = DummyStruct { /// hex_value: ArrayOfHex::try_new([10u8, 1, 11, 2, 12]) /// .unwrap() /// .into(), /// }; /// /// // WHEN /// let string = serde_json::to_string(&example_struct); /// /// // THEN /// assert_eq!(string.unwrap(), r#"{"hex_value":"a1b2c"}"#); /// ``` fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.to_string()) } } impl std::str::FromStr for ArrayOfBase { type Err = String; /// Parse a hexadecimal string into [`ArrayOfHex`] /// /// # Restrictions /// /// This function only works if: /// /// - The base `B` of the [`ArrayOfBase`] is `16` or [`ArrayOfHex`] is used /// /// # Example /// ``` /// # use array_of_base::ArrayOfHex; /// # const ALL_HEX_VALUES: fn() -> ArrayOfHex<16> = || { /// # ArrayOfHex::try_new(core::array::from_fn(|i| i as u8)).unwrap() /// # }; /// # /// // GIVEN /// let string = "0123456789AbCdEf"; /// /// // WHEN /// let actual = string.parse::>(); /// /// // THEN /// assert_eq!( /// actual.ok().unwrap().borrow_u8_array(), /// ALL_HEX_VALUES().borrow_u8_array() /// ); /// ``` fn from_str(s: &str) -> Result { if s.len() != N { return Err(format!( "1669744337 - String does not match expected length of {N}" )); } let mut array = [0; N]; for (i, v) in s.chars().enumerate() { array[i] = match v { '0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, 'a' | 'A' => 10, 'b' | 'B' => 11, 'c' | 'C' => 12, 'd' | 'D' => 13, 'e' | 'E' => 14, 'f' | 'F' => 15, v => { return Err(format!( "1669744576 - Unrecognized char `{v}` at position {i}" )); } }; } ArrayOfHex::try_new(array).map_err(|_| { unreachable!("1669744870 - Input values already validated") }) } } #[cfg(feature = "serde")] impl<'de, const N: usize> Deserialize<'de> for ArrayOfBase { /// Allow using [`ArrayOfHex`] in structs that derive [`serde::Deserialize`] /// /// # Restrictions /// /// This function only works if: /// /// - The crate feature `serde` is activated /// - The base `B` of the [`ArrayOfBase`] is `16` or [`ArrayOfHex`] is used /// /// # Example /// /// ``` /// # use array_of_base::ArrayOfHex; /// # /// # #[derive(serde::Deserialize)] /// # struct DummyStruct { /// # hex_value: ArrayOfHex<5>, /// # } /// # /// // GIVEN /// let example_string = r#"{"hex_value":"A1b2C"}"#; /// /// // WHEN /// let parsed = serde_json::from_str::(example_string); /// /// // THEN /// assert_eq!( /// parsed.ok().unwrap().hex_value.borrow_u8_array(), /// &[10u8, 1, 11, 2, 12] /// ); /// ``` fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { let s = String::deserialize(deserializer)?; std::str::FromStr::from_str(&s).map_err(serde::de::Error::custom) } } fn array_from_iter_unchecked>( mut i: I, ) -> [u8; N] { std::array::from_fn(|_| i.next().unwrap_or_default()) } /// Convert u8 into 2 u4 encoded as u8 /// /// # Example /// #[must_use] fn u8_to_u4_as_u8(v: u8) -> [u8; 2] { [(v & 0xf0) >> 4, v & 0x0f] } impl ArrayOfBase { /// Split [`u8`] values into `u4` values, construct into [`ArrayOfHex`] /// /// # Todo /// /// TODO: Replace with `from_u8_array(array: [u8; {N / 2}])` when `generic_const_exprs` feature stabilizes /// /// # Panics /// /// - If the `I` value is not half `N` the function will panic. /// - If `N` is not an even number, the function will panic. /// /// This will be resolved when `generic_const_exprs` feature stabilizes /// /// # Example /// /// ``` /// # use array_of_base::ArrayOfHex; /// // GIVEN /// let u8_array = [0u8, 16, 255]; /// /// // WHEN /// let actual = ArrayOfHex::<6>::from_u8_array(u8_array); /// // ^^^^^ - Required until `generic_const_exprs` feature stabilizes /// /// // THEN /// assert_eq!(actual.borrow_u8_array(), &[0u8, 0, 1, 0, 15, 15]); /// ``` pub fn from_u8_array(array: [u8; I]) -> ArrayOfHex { assert!(N == I * 2); ArrayOfBase { value: array_from_iter_unchecked( array.into_iter().flat_map(u8_to_u4_as_u8), ), } } } #[cfg(test)] mod test { use super::*; #[test] fn test1669773971_u8_to_u4_as_u8() { // GIVEN let x = 0xab; // WHEN let actual = u8_to_u4_as_u8(x); // THEN assert_eq!(actual, [0xa, 0xb]); } }