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 [`ChannelNumber`] [`Attribute`].
14///
15/// Typically used when setting a TURN channel using the
16/// [`CHANNEL_BIND`](crate::message::CHANNEL_BIND) method.
17///
18/// Reference: [RFC5766 section 14.1](https://datatracker.ietf.org/doc/html/rfc5766#section-14.1).
19#[derive(Debug, Clone)]
20pub struct ChannelNumber {
21    channel: u16,
22}
23
24impl AttributeStaticType for ChannelNumber {
25    const TYPE: AttributeType = AttributeType::new(0x000C);
26}
27
28impl Attribute for ChannelNumber {
29    fn get_type(&self) -> AttributeType {
30        Self::TYPE
31    }
32
33    fn length(&self) -> u16 {
34        4
35    }
36}
37
38impl AttributeWrite for ChannelNumber {
39    fn to_raw(&self) -> RawAttribute<'_> {
40        let mut buf = [0; 4];
41        BigEndian::write_u16(&mut buf[..2], self.channel);
42        RawAttribute::new(self.get_type(), &buf).into_owned()
43    }
44    fn write_into_unchecked(&self, dest: &mut [u8]) {
45        self.write_header_unchecked(dest);
46        BigEndian::write_u16(&mut dest[4..6], self.channel);
47    }
48}
49
50impl AttributeFromRaw<'_> for ChannelNumber {
51    fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
52    where
53        Self: Sized,
54    {
55        Self::try_from(raw)
56    }
57}
58
59impl TryFrom<&RawAttribute<'_>> for ChannelNumber {
60    type Error = StunParseError;
61    fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
62        raw.check_type_and_len(Self::TYPE, 4..=4)?;
63        Ok(Self {
64            channel: BigEndian::read_u16(&raw.value),
65        })
66    }
67}
68
69impl ChannelNumber {
70    /// Create a new [`ChannelNumber`] [`Attribute`].
71    ///
72    /// # Examples
73    ///
74    /// ```
75    /// # use turn_types::attribute::*;
76    /// let channel = ChannelNumber::new (42);
77    /// assert_eq!(channel.channel(), 42);
78    /// ```
79    pub fn new(channel: u16) -> Self {
80        Self { channel }
81    }
82
83    /// The channel number stored in a [`ChannelNumber`] [`Attribute`].
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// # use turn_types::attribute::*;
89    /// let channel = ChannelNumber::new (42);
90    /// assert_eq!(channel.channel(), 42);
91    /// ```
92    pub fn channel(&self) -> u16 {
93        self.channel
94    }
95}
96
97impl std::fmt::Display for ChannelNumber {
98    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99        write!(f, "{}: '{}'", self.get_type(), self.channel)
100    }
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106    use byteorder::{BigEndian, ByteOrder};
107
108    #[test]
109    fn channel_number() {
110        let _log = crate::tests::test_init_log();
111        let c = ChannelNumber::new(6);
112        assert_eq!(c.get_type(), ChannelNumber::TYPE);
113        assert_eq!(c.channel(), 6);
114        let raw: RawAttribute = c.to_raw();
115        println!("{}", raw);
116        assert_eq!(raw.get_type(), ChannelNumber::TYPE);
117        let c2 = ChannelNumber::try_from(&raw).unwrap();
118        assert_eq!(c2.get_type(), ChannelNumber::TYPE);
119        assert_eq!(c2.channel(), 6);
120        // provide incorrectly typed data
121        let mut data: Vec<_> = raw.into();
122        BigEndian::write_u16(&mut data[0..2], 0);
123        assert!(matches!(
124            ChannelNumber::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
125            Err(StunParseError::WrongAttributeImplementation)
126        ));
127    }
128}