use crate::err::ValueTooBigError; /// 6 bit unsigned integer containing the "Differentiated Services /// Code Point" (present in the [`crate::Ipv4Header`]). #[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct Ipv4Dscp(u8); impl Ipv4Dscp { /// Ipv4Dscp with value 0. pub const ZERO: Ipv4Dscp = Ipv4Dscp(0); /// Maximum value of an IPv4 header DSCP. pub const MAX_U8: u8 = 0b0011_1111; /// Tries to create an [`Ipv4Dscp`] and checks that the passed value /// is smaller or equal than [`Ipv4Dscp::MAX_U8`] (6 bit unsigned integer). /// /// In case the passed value is bigger then what can be represented in an 6 bit /// integer an error is returned. Otherwise an `Ok` containing the [`Ipv4Dscp`]. /// /// ``` /// use etherparse::Ipv4Dscp; /// /// let dscp = Ipv4Dscp::try_new(32).unwrap(); /// assert_eq!(dscp.value(), 32); /// /// // if a number that can not be represented in an 6 bit integer /// // gets passed in an error is returned /// use etherparse::err::{ValueTooBigError, ValueType}; /// assert_eq!( /// Ipv4Dscp::try_new(Ipv4Dscp::MAX_U8 + 1), /// Err(ValueTooBigError{ /// actual: Ipv4Dscp::MAX_U8 + 1, /// max_allowed: Ipv4Dscp::MAX_U8, /// value_type: ValueType::Ipv4Dscp, /// }) /// ); /// ``` #[inline] pub const fn try_new(value: u8) -> Result> { use crate::err::ValueType; if value <= Ipv4Dscp::MAX_U8 { Ok(Ipv4Dscp(value)) } else { Err(ValueTooBigError { actual: value, max_allowed: Ipv4Dscp::MAX_U8, value_type: ValueType::Ipv4Dscp, }) } } /// Creates an [`Ipv4Dscp`] without checking that the value /// is smaller or equal than [`Ipv4Dscp::MAX_U8`] (6 bit unsigned integer). /// The caller must guarantee that `value <= Ipv4Dscp::MAX_U8`. /// /// # Safety /// /// `value` must be smaller or equal than [`Ipv4Dscp::MAX_U8`] /// otherwise the behavior of functions or data structures relying /// on this pre-requirement is undefined. #[inline] pub const unsafe fn new_unchecked(value: u8) -> Ipv4Dscp { debug_assert!(value <= Ipv4Dscp::MAX_U8); Ipv4Dscp(value) } /// Returns the underlying unsigned 6 bit value as an `u8` value. #[inline] pub const fn value(self) -> u8 { self.0 } } impl core::fmt::Display for Ipv4Dscp { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { self.0.fmt(f) } } impl From for u8 { #[inline] fn from(value: Ipv4Dscp) -> Self { value.0 } } impl TryFrom for Ipv4Dscp { type Error = ValueTooBigError; #[inline] fn try_from(value: u8) -> Result { use crate::err::ValueType; if value <= Ipv4Dscp::MAX_U8 { Ok(Ipv4Dscp(value)) } else { Err(Self::Error { actual: value, max_allowed: Ipv4Dscp::MAX_U8, value_type: ValueType::Ipv4Dscp, }) } } } #[cfg(test)] mod test { use super::*; use core::hash::{Hash, Hasher}; use proptest::prelude::*; use std::format; #[test] fn derived_traits() { // copy & clone { let a = Ipv4Dscp(32); let b = a; assert_eq!(a, b); assert_eq!(a.clone(), a); } // default { let actual: Ipv4Dscp = Default::default(); assert_eq!(actual.value(), 0); } // debug { let a = Ipv4Dscp(32); assert_eq!(format!("{:?}", a), format!("Ipv4Dscp(32)")); } // ord & partial ord { use core::cmp::Ordering; let a = Ipv4Dscp(32); let b = a; assert_eq!(a.cmp(&b), Ordering::Equal); assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal)); } // hash { use std::collections::hash_map::DefaultHasher; let a = { let mut hasher = DefaultHasher::new(); Ipv4Dscp(64).hash(&mut hasher); hasher.finish() }; let b = { let mut hasher = DefaultHasher::new(); Ipv4Dscp(64).hash(&mut hasher); hasher.finish() }; assert_eq!(a, b); } } proptest! { #[test] fn try_new( valid_value in 0..=0b0011_1111u8, invalid_value in 0b0100_0000u8..=u8::MAX ) { use crate::err::{ValueType, ValueTooBigError}; assert_eq!( valid_value, Ipv4Dscp::try_new(valid_value).unwrap().value() ); assert_eq!( Ipv4Dscp::try_new(invalid_value).unwrap_err(), ValueTooBigError{ actual: invalid_value, max_allowed: 0b0011_1111, value_type: ValueType::Ipv4Dscp } ); } } proptest! { #[test] fn try_from( valid_value in 0..=0b0011_1111u8, invalid_value in 0b0100_0000u8..=u8::MAX ) { use crate::err::{ValueType, ValueTooBigError}; // try_into { let actual: Ipv4Dscp = valid_value.try_into().unwrap(); assert_eq!(actual.value(), valid_value); let err: Result> = invalid_value.try_into(); assert_eq!( err.unwrap_err(), ValueTooBigError{ actual: invalid_value, max_allowed: 0b0011_1111, value_type: ValueType::Ipv4Dscp } ); } // try_from { assert_eq!( Ipv4Dscp::try_from(valid_value).unwrap().value(), valid_value ); assert_eq!( Ipv4Dscp::try_from(invalid_value).unwrap_err(), ValueTooBigError{ actual: invalid_value, max_allowed: 0b0011_1111, value_type: ValueType::Ipv4Dscp } ); } } } proptest! { #[test] fn new_unchecked(valid_value in 0..=0b0011_1111u8) { assert_eq!( valid_value, unsafe { Ipv4Dscp::new_unchecked(valid_value).value() } ); } } proptest! { #[test] fn fmt(valid_value in 0..=0b0011_1111u8) { assert_eq!(format!("{}", Ipv4Dscp(valid_value)), format!("{}", valid_value)); } } proptest! { #[test] fn from(valid_value in 0..=0b0011_1111u8,) { let dscp = Ipv4Dscp::try_new(valid_value).unwrap(); let actual: u8 = dscp.into(); assert_eq!(actual, valid_value); } } }