turn_types/attribute/
channel.rs

1// Copyright (C) 2025 Matthew Waters <matthew@centricular.com>
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use byteorder::{BigEndian, ByteOrder};
10
11use stun_types::{attribute::*, message::StunParseError};
12
13/// The channel number [`Attribute`]
14#[derive(Debug, Clone)]
15pub struct ChannelNumber {
16    channel: u16,
17}
18
19impl AttributeStaticType for ChannelNumber {
20    const TYPE: AttributeType = AttributeType::new(0x000C);
21}
22
23impl Attribute for ChannelNumber {
24    fn get_type(&self) -> AttributeType {
25        Self::TYPE
26    }
27
28    fn length(&self) -> u16 {
29        4
30    }
31}
32
33impl AttributeWrite for ChannelNumber {
34    fn to_raw(&self) -> RawAttribute {
35        let mut buf = [0; 4];
36        BigEndian::write_u16(&mut buf[..2], self.channel);
37        RawAttribute::new(self.get_type(), &buf).into_owned()
38    }
39    fn write_into_unchecked(&self, dest: &mut [u8]) {
40        self.write_header_unchecked(dest);
41        BigEndian::write_u16(&mut dest[4..6], self.channel);
42    }
43}
44
45impl AttributeFromRaw<'_> for ChannelNumber {
46    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
47    where
48        Self: Sized,
49    {
50        Self::try_from(raw)
51    }
52}
53
54impl TryFrom<&RawAttribute<'_>> for ChannelNumber {
55    type Error = StunParseError;
56    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
57        raw.check_type_and_len(Self::TYPE, 4..=4)?;
58        Ok(Self {
59            channel: BigEndian::read_u16(&raw.value),
60        })
61    }
62}
63
64impl ChannelNumber {
65    /// Create a new [`ChannelNumber`] [`Attribute`]
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// # use turn_types::attribute::*;
71    /// let channel = ChannelNumber::new (42);
72    /// assert_eq!(channel.channel(), 42);
73    /// ```
74    pub fn new(channel: u16) -> Self {
75        Self { channel }
76    }
77
78    /// The channel number stored in a [`ChannelNumber`] [`Attribute`]
79    ///
80    /// # Examples
81    ///
82    /// ```
83    /// # use turn_types::attribute::*;
84    /// let channel = ChannelNumber::new (42);
85    /// assert_eq!(channel.channel(), 42);
86    /// ```
87    pub fn channel(&self) -> u16 {
88        self.channel
89    }
90}
91
92impl std::fmt::Display for ChannelNumber {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        write!(f, "{}: '{}'", self.get_type(), self.channel)
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use byteorder::{BigEndian, ByteOrder};
102
103    #[test]
104    fn channel_number() {
105        let _log = crate::tests::test_init_log();
106        let c = ChannelNumber::new(6);
107        assert_eq!(c.get_type(), ChannelNumber::TYPE);
108        assert_eq!(c.channel(), 6);
109        let raw: RawAttribute = c.to_raw();
110        println!("{}", raw);
111        assert_eq!(raw.get_type(), ChannelNumber::TYPE);
112        let c2 = ChannelNumber::try_from(&raw).unwrap();
113        assert_eq!(c2.get_type(), ChannelNumber::TYPE);
114        assert_eq!(c2.channel(), 6);
115        // provide incorrectly typed data
116        let mut data: Vec<_> = raw.into();
117        BigEndian::write_u16(&mut data[0..2], 0);
118        assert!(matches!(
119            ChannelNumber::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
120            Err(StunParseError::WrongAttributeImplementation)
121        ));
122    }
123}