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