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