blob: d5d5bfd53bff2d38861495cdb3f928d2a1d77068 [file] [log] [blame]
use crate::err::ValueTooBigError;
/// 12 bit unsigned integer containing the "VLAN identifier" (present
/// in the [`crate::SingleVlanHeader`]).
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct VlanId(u16);
impl VlanId {
/// VlanId with value 0.
pub const ZERO: VlanId = VlanId(0);
/// Maximum value of an VLAN id.
pub const MAX_U16: u16 = 0b0000_1111_1111_1111;
/// Tries to create an [`VlanId`] and checks that the passed value
/// is smaller or equal than [`VlanId::MAX_U16`] (12 bit unsigned integer).
///
/// In case the passed value is bigger then what can be represented in an 12 bit
/// integer an error is returned. Otherwise an `Ok` containing the [`VlanId`].
///
/// ```
/// use etherparse::VlanId;
///
/// let vlanid = VlanId::try_new(2).unwrap();
/// assert_eq!(vlanid.value(), 2);
///
/// // if a number that can not be represented in an 12 bit integer
/// // gets passed in an error is returned
/// use etherparse::err::{ValueTooBigError, ValueType};
/// assert_eq!(
/// VlanId::try_new(VlanId::MAX_U16 + 1),
/// Err(ValueTooBigError{
/// actual: VlanId::MAX_U16 + 1,
/// max_allowed: VlanId::MAX_U16,
/// value_type: ValueType::VlanId,
/// })
/// );
/// ```
#[inline]
pub const fn try_new(value: u16) -> Result<VlanId, ValueTooBigError<u16>> {
use crate::err::ValueType;
if value <= VlanId::MAX_U16 {
Ok(VlanId(value))
} else {
Err(ValueTooBigError {
actual: value,
max_allowed: VlanId::MAX_U16,
value_type: ValueType::VlanId,
})
}
}
/// Creates an [`VlanId`] WITHOUT checking that the value
/// is smaller or equal than [`VlanId::MAX_U16`] (12 bit unsigned integer).
/// The caller must guarantee that `value <= VlanId::MAX_U16`.
///
/// # Safety
///
/// `value` must be smaller or equal than [`VlanId::MAX_U16`]
/// otherwise the behavior of functions or data structures relying
/// on this pre-requirement is undefined.
#[inline]
pub const unsafe fn new_unchecked(value: u16) -> VlanId {
debug_assert!(value <= VlanId::MAX_U16);
VlanId(value)
}
/// Returns the underlying unsigned 12 bit value as an `u16` value.
#[inline]
pub const fn value(self) -> u16 {
self.0
}
}
impl core::fmt::Display for VlanId {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.0.fmt(f)
}
}
impl From<VlanId> for u16 {
#[inline]
fn from(value: VlanId) -> Self {
value.0
}
}
impl TryFrom<u16> for VlanId {
type Error = ValueTooBigError<u16>;
#[inline]
fn try_from(value: u16) -> Result<Self, Self::Error> {
use crate::err::ValueType;
if value <= VlanId::MAX_U16 {
Ok(VlanId(value))
} else {
Err(Self::Error {
actual: value,
max_allowed: VlanId::MAX_U16,
value_type: ValueType::VlanId,
})
}
}
}
#[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 = VlanId(2);
let b = a;
assert_eq!(a, b);
assert_eq!(a.clone(), a);
}
// default
{
let actual: VlanId = Default::default();
assert_eq!(actual.value(), 0);
}
// debug
{
let a = VlanId(2);
assert_eq!(format!("{:?}", a), format!("VlanId(2)"));
}
// ord & partial ord
{
use core::cmp::Ordering;
let a = VlanId(2);
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();
VlanId(2).hash(&mut hasher);
hasher.finish()
};
let b = {
let mut hasher = DefaultHasher::new();
VlanId(2).hash(&mut hasher);
hasher.finish()
};
assert_eq!(a, b);
}
}
proptest! {
#[test]
fn try_new(
valid_value in 0..=0b0000_1111_1111_1111u16,
invalid_value in 0b0001_0000_0000_0000u16..=u16::MAX
) {
use crate::err::{ValueType, ValueTooBigError};
assert_eq!(
valid_value,
VlanId::try_new(valid_value).unwrap().value()
);
assert_eq!(
VlanId::try_new(invalid_value).unwrap_err(),
ValueTooBigError{
actual: invalid_value,
max_allowed: 0b0000_1111_1111_1111,
value_type: ValueType::VlanId
}
);
}
}
proptest! {
#[test]
fn try_from(
valid_value in 0..=0b0000_1111_1111_1111u16,
invalid_value in 0b0001_0000_0000_0000u16..=u16::MAX
) {
use crate::err::{ValueType, ValueTooBigError};
// try_into
{
let actual: VlanId = valid_value.try_into().unwrap();
assert_eq!(actual.value(), valid_value);
let err: Result<VlanId, ValueTooBigError<u16>> = invalid_value.try_into();
assert_eq!(
err.unwrap_err(),
ValueTooBigError{
actual: invalid_value,
max_allowed: 0b0000_1111_1111_1111,
value_type: ValueType::VlanId
}
);
}
// try_from
{
assert_eq!(
VlanId::try_from(valid_value).unwrap().value(),
valid_value
);
assert_eq!(
VlanId::try_from(invalid_value).unwrap_err(),
ValueTooBigError{
actual: invalid_value,
max_allowed: 0b0000_1111_1111_1111,
value_type: ValueType::VlanId
}
);
}
}
}
proptest! {
#[test]
fn new_unchecked(valid_value in 0..=0b0000_1111_1111_1111u16) {
assert_eq!(
valid_value,
unsafe {
VlanId::new_unchecked(valid_value).value()
}
);
}
}
proptest! {
#[test]
fn fmt(valid_value in 0..=0b0000_1111_1111_1111u16) {
assert_eq!(format!("{}", VlanId(valid_value)), format!("{}", valid_value));
}
}
proptest! {
#[test]
fn from(valid_value in 0..=0b0000_1111_1111_1111u16,) {
let vlanid = VlanId::try_new(valid_value).unwrap();
let actual: u16 = vlanid.into();
assert_eq!(actual, valid_value);
}
}
}