use byteorder::{BigEndian, ByteOrder};
use stun_types::{attribute::*, message::StunParseError};
#[derive(Debug, Clone)]
pub struct ChannelNumber {
channel: u16,
}
impl AttributeStaticType for ChannelNumber {
const TYPE: AttributeType = AttributeType::new(0x000C);
}
impl Attribute for ChannelNumber {
fn get_type(&self) -> AttributeType {
Self::TYPE
}
fn length(&self) -> u16 {
4
}
}
impl AttributeWrite for ChannelNumber {
fn to_raw(&self) -> RawAttribute<'_> {
let mut buf = [0; 4];
BigEndian::write_u16(&mut buf[..2], self.channel);
RawAttribute::new(self.get_type(), &buf).into_owned()
}
fn write_into_unchecked(&self, dest: &mut [u8]) {
self.write_header_unchecked(dest);
BigEndian::write_u16(&mut dest[4..6], self.channel);
dest[7..self.padded_len()].fill(0);
}
}
impl AttributeFromRaw<'_> for ChannelNumber {
fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
where
Self: Sized,
{
Self::try_from(raw)
}
}
impl TryFrom<&RawAttribute<'_>> for ChannelNumber {
type Error = StunParseError;
fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
raw.check_type_and_len(Self::TYPE, 4..=4)?;
Ok(Self {
channel: BigEndian::read_u16(&raw.value),
})
}
}
impl ChannelNumber {
pub fn new(channel: u16) -> Self {
Self { channel }
}
pub fn channel(&self) -> u16 {
self.channel
}
}
impl core::fmt::Display for ChannelNumber {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}: '{}'", self.get_type(), self.channel)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::{vec, vec::Vec};
use byteorder::{BigEndian, ByteOrder};
use tracing::trace;
#[test]
fn channel_number() {
let _log = crate::tests::test_init_log();
let c = ChannelNumber::new(6);
assert_eq!(c.get_type(), ChannelNumber::TYPE);
assert_eq!(c.channel(), 6);
}
#[test]
fn channel_number_raw() {
let _log = crate::tests::test_init_log();
let c = ChannelNumber::new(6);
assert_eq!(c.get_type(), ChannelNumber::TYPE);
assert_eq!(c.channel(), 6);
let raw: RawAttribute = c.to_raw();
trace!("{}", raw);
assert_eq!(raw.get_type(), ChannelNumber::TYPE);
let c2 = ChannelNumber::try_from(&raw).unwrap();
assert_eq!(c2.get_type(), ChannelNumber::TYPE);
assert_eq!(c2.channel(), 6);
}
#[test]
fn channel_number_raw_wrong_type() {
let _log = crate::tests::test_init_log();
let c = ChannelNumber::new(6);
assert_eq!(c.get_type(), ChannelNumber::TYPE);
assert_eq!(c.channel(), 6);
let raw: RawAttribute = c.to_raw();
let mut data: Vec<_> = raw.into();
BigEndian::write_u16(&mut data[0..2], 0);
assert!(matches!(
ChannelNumber::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
Err(StunParseError::WrongAttributeImplementation)
));
}
#[test]
fn channel_number_write_into() {
let _log = crate::tests::test_init_log();
let c = ChannelNumber::new(6);
assert_eq!(c.channel(), 6);
let raw: RawAttribute = c.to_raw();
let mut dest = vec![0; raw.padded_len()];
c.write_into(&mut dest).unwrap();
let raw = RawAttribute::from_bytes(&dest).unwrap();
let c2 = ChannelNumber::try_from(&raw).unwrap();
assert_eq!(c2.get_type(), ChannelNumber::TYPE);
assert_eq!(c2.channel(), c.channel());
}
#[test]
#[should_panic = "out of range"]
fn channel_number_write_into_unchecked() {
let _log = crate::tests::test_init_log();
let c = ChannelNumber::new(6);
assert_eq!(c.channel(), 6);
let raw: RawAttribute = c.to_raw();
let mut dest = vec![0; raw.padded_len() - 1];
c.write_into_unchecked(&mut dest);
}
}