blob: 9498b34245658303c66d64da52cfeac1fb29636b [file] [log] [blame]
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<Ipv4Dscp, ValueTooBigError<u8>> {
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<Ipv4Dscp> for u8 {
#[inline]
fn from(value: Ipv4Dscp) -> Self {
value.0
}
}
impl TryFrom<u8> for Ipv4Dscp {
type Error = ValueTooBigError<u8>;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
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<Ipv4Dscp, ValueTooBigError<u8>> = 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);
}
}
}