stun_types/attribute/
software.rs1use alloc::borrow::ToOwned;
12use alloc::string::String;
13use core::convert::TryFrom;
14
15use crate::message::{StunParseError, StunWriteError};
16
17use super::{
18 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
19 AttributeWriteExt, RawAttribute,
20};
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Software {
25 software: String,
26}
27
28impl AttributeStaticType for Software {
29 const TYPE: AttributeType = AttributeType(0x8022);
30}
31
32impl Attribute for Software {
33 fn get_type(&self) -> AttributeType {
34 Self::TYPE
35 }
36
37 fn length(&self) -> u16 {
38 self.software.len() as u16
39 }
40}
41
42impl AttributeWrite for Software {
43 fn to_raw(&self) -> RawAttribute<'_> {
44 RawAttribute::new(Software::TYPE, self.software.as_bytes())
45 }
46
47 fn write_into_unchecked(&self, dest: &mut [u8]) {
48 let len = self.padded_len();
49 let offset = self.write_header_unchecked(dest);
50 dest[offset..offset + self.software.len()].copy_from_slice(self.software.as_bytes());
51 let offset = offset + self.software.len();
52 if len > offset {
53 dest[offset..len].fill(0);
54 }
55 }
56}
57
58impl AttributeFromRaw<'_> for Software {
59 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
60 where
61 Self: Sized,
62 {
63 Self::try_from(raw)
64 }
65}
66
67impl TryFrom<&RawAttribute<'_>> for Software {
68 type Error = StunParseError;
69
70 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
71 raw.check_type_and_len(Self::TYPE, ..=763)?;
72 Ok(Self {
73 software: core::str::from_utf8(&raw.value)
74 .map_err(|_| StunParseError::InvalidAttributeData)?
75 .to_owned(),
76 })
77 }
78}
79
80impl Software {
81 pub fn new(software: &str) -> Result<Self, StunWriteError> {
95 if software.len() > 763 {
97 return Err(StunWriteError::TooLarge {
98 expected: 763,
99 actual: software.len(),
100 });
101 }
102 Ok(Self {
103 software: software.to_owned(),
104 })
105 }
106
107 pub fn software(&self) -> &str {
117 &self.software
118 }
119}
120
121impl core::fmt::Display for Software {
122 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123 write!(f, "{}: '{}'", Software::TYPE, self.software)
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use alloc::vec;
131 use alloc::vec::Vec;
132 use byteorder::{BigEndian, ByteOrder};
133 use tracing::trace;
134
135 #[test]
136 fn software() {
137 let _log = crate::tests::test_init_log();
138 let software = Software::new("software").unwrap();
139 trace!("{software}");
140 assert_eq!(software.software(), "software");
141 assert_eq!(software.length() as usize, "software".len());
142 }
143
144 #[test]
145 fn software_raw() {
146 let _log = crate::tests::test_init_log();
147 let software = Software::new("software").unwrap();
148 let raw = RawAttribute::from(&software);
149 trace!("{raw}");
150 assert_eq!(raw.get_type(), Software::TYPE);
151 let software2 = Software::try_from(&raw).unwrap();
152 assert_eq!(software2.software(), "software");
153 }
154
155 #[test]
156 fn software_raw_wrong_type() {
157 let _log = crate::tests::test_init_log();
158 let software = Software::new("software").unwrap();
159 let raw = RawAttribute::from(&software);
160
161 let mut data: Vec<_> = raw.into();
163 BigEndian::write_u16(&mut data[0..2], 0);
164 assert!(matches!(
165 Software::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
166 Err(StunParseError::WrongAttributeImplementation)
167 ));
168 }
169
170 #[test]
171 fn software_write_into() {
172 let _log = crate::tests::test_init_log();
173 let software = Software::new("software").unwrap();
174 let raw = RawAttribute::from(&software);
175
176 let mut dest = vec![0; raw.padded_len()];
177 software.write_into(&mut dest).unwrap();
178 let raw = RawAttribute::from_bytes(&dest).unwrap();
179 let software2 = Software::try_from(&raw).unwrap();
180 assert_eq!(software2.software(), "software");
181 }
182
183 #[test]
184 #[should_panic(expected = "out of range")]
185 fn software_write_into_unchcked() {
186 let _log = crate::tests::test_init_log();
187 let software = Software::new("software").unwrap();
188 let raw = RawAttribute::from(&software);
189
190 let mut dest = vec![0; raw.padded_len() - 1];
191 software.write_into_unchecked(&mut dest);
192 }
193
194 #[test]
195 fn software_not_utf8() {
196 let _log = crate::tests::test_init_log();
197 let attr = Software::new("software").unwrap();
198 let raw = RawAttribute::from(&attr);
199 let mut data = raw.to_bytes();
200 data[6] = 0x88;
201 assert!(matches!(
202 Software::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
203 Err(StunParseError::InvalidAttributeData)
204 ));
205 }
206
207 #[test]
208 fn software_new_too_large() {
209 let _log = crate::tests::test_init_log();
210 let mut large = String::new();
211 for _i in 0..95 {
212 large.push_str("abcdefgh");
213 }
214 large.push_str("abcd");
215 assert!(matches!(
216 Software::new(&large),
217 Err(StunWriteError::TooLarge {
218 expected: 763,
219 actual: 764
220 })
221 ));
222 }
223}