1use arbitrary::Arbitrary;
6use fidl_fuchsia_wlan_ieee80211::{self as fidl_ieee80211, MAX_SSID_BYTE_LEN};
7use std::borrow::Cow;
8use std::ops::{Deref, Index};
9use std::slice::SliceIndex;
10use std::{fmt, str};
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14#[cfg_attr(test, derive(PartialEq, Eq))]
15#[non_exhaustive]
16pub enum SsidError {
17 #[error("Invalid SSID length: {0} bytes (maximum is {MAX_SSID_BYTE_LEN})")]
18 Size(usize),
19}
20
21#[derive(Arbitrary)] #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
52pub struct Ssid(Box<[u8]>);
53
54impl AsRef<[u8]> for Ssid {
55 fn as_ref(&self) -> &[u8] {
56 &self.0[..]
57 }
58}
59
60impl Deref for Ssid {
61 type Target = [u8];
62
63 fn deref(&self) -> &[u8] {
64 &self.0
65 }
66}
67
68impl fmt::Display for Ssid {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 write!(f, "<ssid-{}>", hex::encode(self.0.clone()))
73 }
74}
75
76impl From<Ssid> for String {
77 fn from(ssid: Ssid) -> String {
78 ssid.to_string()
79 }
80}
81
82impl From<Ssid> for Vec<u8> {
83 fn from(ssid: Ssid) -> Vec<u8> {
84 ssid.0.into_vec()
85 }
86}
87
88impl<Idx> Index<Idx> for Ssid
89where
90 Idx: SliceIndex<[u8]>,
91{
92 type Output = Idx::Output;
93
94 fn index(&self, index: Idx) -> &Self::Output {
95 &self.0[index]
96 }
97}
98
99impl PartialEq<Ssid> for Vec<u8> {
100 fn eq(&self, other: &Ssid) -> bool {
101 Ssid::deref(other)[..] == self[..]
102 }
103}
104
105impl PartialEq<Vec<u8>> for Ssid {
106 fn eq(&self, other: &Vec<u8>) -> bool {
107 Ssid::deref(self)[..] == other[..]
108 }
109}
110
111impl<const N: usize> PartialEq<[u8; N]> for Ssid {
112 fn eq(&self, other: &[u8; N]) -> bool {
113 Ssid::deref(self)[..] == other[..]
114 }
115}
116
117impl<const N: usize> PartialEq<Ssid> for [u8; N] {
118 fn eq(&self, other: &Ssid) -> bool {
119 Ssid::deref(other)[..] == self[..]
120 }
121}
122
123impl PartialEq<[u8]> for Ssid {
124 fn eq(&self, other: &[u8]) -> bool {
125 Ssid::deref(self)[..] == other[..]
126 }
127}
128
129impl PartialEq<Ssid> for [u8] {
130 fn eq(&self, other: &Ssid) -> bool {
131 Ssid::deref(other)[..] == self[..]
132 }
133}
134
135impl TryFrom<Box<[u8]>> for Ssid {
136 type Error = SsidError;
137
138 fn try_from(bytes: Box<[u8]>) -> Result<Self, Self::Error> {
139 Ssid::check(&bytes)?;
140 Ok(Ssid(bytes))
141 }
142}
143
144impl<const N: usize> TryFrom<[u8; N]> for Ssid {
145 type Error = SsidError;
146
147 fn try_from(bytes: [u8; N]) -> Result<Self, Self::Error> {
148 Ssid::check(&bytes)?;
149 Ok(Ssid(bytes.into()))
150 }
151}
152
153impl TryFrom<Box<str>> for Ssid {
154 type Error = SsidError;
155
156 fn try_from(s: Box<str>) -> Result<Self, Self::Error> {
157 s.into_boxed_bytes().try_into()
158 }
159}
160
161impl TryFrom<String> for Ssid {
162 type Error = SsidError;
163
164 fn try_from(s: String) -> Result<Self, Self::Error> {
165 s.into_boxed_str().try_into()
166 }
167}
168
169impl TryFrom<Vec<u8>> for Ssid {
170 type Error = SsidError;
171
172 fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
173 bytes.into_boxed_slice().try_into()
174 }
175}
176
177impl TryFrom<&str> for Ssid {
178 type Error = SsidError;
179
180 fn try_from(s: &str) -> Result<Self, Self::Error> {
181 Ssid::check(s.as_bytes())?;
182 Ok(Ssid(String::from(s).into_boxed_str().into_boxed_bytes()))
183 }
184}
185
186impl TryFrom<&[u8]> for Ssid {
187 type Error = SsidError;
188
189 fn try_from(s: &[u8]) -> Result<Self, Self::Error> {
190 Ssid::check(s)?;
191 Ok(Ssid(s.to_vec().into_boxed_slice()))
192 }
193}
194
195impl Ssid {
196 pub fn from_bytes_unchecked(ssid_bytes: Vec<u8>) -> Ssid {
206 Ssid(ssid_bytes.into_boxed_slice())
207 }
208
209 pub fn to_string_not_redactable(&self) -> Cow<'_, str> {
212 str::from_utf8(self.as_ref()).map(From::from).unwrap_or_else(|_| self.to_string().into())
213 }
214
215 pub fn empty() -> Ssid {
216 Ssid([].into())
217 }
218
219 pub fn len(&self) -> usize {
220 self.0.len()
221 }
222
223 pub fn to_vec(&self) -> Vec<u8> {
224 self.0.clone().into()
225 }
226
227 fn check(bytes: &[u8]) -> Result<(), SsidError> {
228 if bytes.len() > (fidl_ieee80211::MAX_SSID_BYTE_LEN as usize) {
229 return Err(SsidError::Size(bytes.len()));
230 }
231 Ok(())
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use super::*;
238 use std::sync::LazyLock;
239
240 static SSID_12345: LazyLock<Ssid> =
241 LazyLock::new(|| Ssid::try_from([0x01, 0x02, 0x03, 0x04, 0x05]).unwrap());
242 static SSID_FOO: LazyLock<Ssid> = LazyLock::new(|| Ssid::try_from([0x66, 0x6F, 0x6F]).unwrap());
243
244 #[test]
245 fn ssid_check() {
246 assert_eq!(Ok(()), Ssid::check(&[0x2; 0]));
247 assert_eq!(Ok(()), Ssid::check(&[0x2; 20]));
248 assert_eq!(Ok(()), Ssid::check(&[0x2; 32]));
249 assert_eq!(Err(SsidError::Size(33)), Ssid::check(&[0x2; 33]));
250 }
251
252 #[test]
253 fn ssid_as_ref() {
254 let array_12345: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05];
255 assert_eq!((*SSID_12345).as_ref(), array_12345.as_ref());
256 }
257
258 #[test]
259 fn ssid_deref() {
260 let array_12345: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05];
261 assert_eq!(**SSID_12345, array_12345);
262 }
263
264 #[test]
265 fn ssid_to_string() {
266 assert_eq!(Ssid::empty().to_string(), "<ssid->");
267 assert_eq!(SSID_FOO.to_string(), "<ssid-666f6f>");
268 assert_eq!(SSID_12345.to_string(), "<ssid-0102030405>");
269 }
270
271 #[test]
272 fn format_ssid() {
273 assert_eq!(format!("{}", *SSID_12345), "<ssid-0102030405>");
274 }
275
276 #[test]
277 fn ssid_into_vec() {
278 assert_eq!(
279 <Ssid as Into<Vec<u8>>>::into(SSID_12345.clone()),
280 vec![0x01, 0x02, 0x03, 0x04, 0x05]
281 );
282 }
283
284 #[test]
285 fn ssid_try_from_boxed_slice_ok() {
286 let ssid: Ssid =
287 <Ssid as TryFrom<Box<[u8]>>>::try_from(Box::from([0x01, 0x02, 0x03, 0x04, 0x05]))
288 .expect("Failed to convert Box<[u8]> to Ssid");
289 assert_eq!(ssid, *SSID_12345);
290 }
291
292 #[test]
293 fn ssid_try_from_array_ok() {
294 let array = [0x01, 0x02, 0x03, 0x04, 0x05];
295 let ssid: Ssid =
296 <Ssid as TryFrom<[u8; 5]>>::try_from(array).expect("Failed to convert [u8; 5] to Ssid");
297 assert_eq!(ssid, *SSID_12345);
298 }
299
300 #[test]
301 fn ssid_try_from_boxed_str_ok() {
302 let ssid: Ssid =
303 <Ssid as TryFrom<Box<str>>>::try_from(String::from("foo").into_boxed_str())
304 .expect("Failed to convert Box<str> to Ssid");
305 assert_eq!(ssid, *SSID_FOO);
306 }
307
308 #[test]
309 fn ssid_try_from_string_ok() {
310 let ssid: Ssid = <Ssid as TryFrom<String>>::try_from(String::from("foo"))
311 .expect("Failed to convert String to Ssid");
312 assert_eq!(ssid, *SSID_FOO);
313 }
314
315 #[test]
316 fn ssid_try_from_vec_ok() {
317 let ssid: Ssid = <Ssid as TryFrom<Vec<u8>>>::try_from(vec![0x01, 0x02, 0x03, 0x04, 0x05])
318 .expect("Failed to convert Vec<u8> to Ssid");
319 assert_eq!(ssid, *SSID_12345);
320 }
321
322 #[test]
323 fn ssid_try_from_str_ok() {
324 let ssid: Ssid =
325 <Ssid as TryFrom<&str>>::try_from("foo").expect("Failed to convert &str to Ssid");
326 assert_eq!(ssid, *SSID_FOO);
327 }
328
329 #[test]
330 fn ssid_try_from_slice_ok() {
331 let ssid: Ssid = <Ssid as TryFrom<&[u8]>>::try_from(&[0x01, 0x02, 0x03, 0x04, 0x05])
332 .expect("Failed to convert &[u8] to Ssid");
333 assert_eq!(ssid, *SSID_12345);
334 }
335
336 #[test]
337 fn ssid_try_from_array_err() {
338 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<[u8; 32]>>::try_from([0x03; 32]);
339 assert!(matches!(ssid, Ok(_)));
340 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<[u8; 50]>>::try_from([0x03; 50]);
341 assert!(matches!(ssid, Err(SsidError::Size(50))));
342 }
343
344 #[test]
345 fn ssid_try_from_string_err() {
346 let ssid: Result<Ssid, SsidError> =
347 <Ssid as TryFrom<String>>::try_from(String::from("12345678901234567890123456789012"));
348 assert!(matches!(ssid, Ok(_)));
349 let ssid: Result<Ssid, SsidError> =
350 <Ssid as TryFrom<String>>::try_from(String::from("123456789012345678901234567890123"));
351 assert!(matches!(ssid, Err(SsidError::Size(33))));
352 }
353
354 #[test]
355 fn ssid_try_from_vec_err() {
356 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<Vec<u8>>>::try_from(vec![0x07; 32]);
357 assert!(matches!(ssid, Ok(_)));
358 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<Vec<u8>>>::try_from(vec![0x07; 100]);
359 assert!(matches!(ssid, Err(SsidError::Size(100))));
360 }
361
362 #[test]
363 fn ssid_try_from_str_err() {
364 let ssid: Result<Ssid, SsidError> =
365 <Ssid as TryFrom<&str>>::try_from("12345678901234567890123456789012");
366 assert!(matches!(ssid, Ok(_)));
367 let ssid: Result<Ssid, SsidError> =
368 <Ssid as TryFrom<&str>>::try_from("123456789012345678901234567890123");
369 assert!(matches!(ssid, Err(SsidError::Size(33))));
370 }
371
372 #[test]
373 fn ssid_try_from_slice_err() {
374 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<&[u8]>>::try_from(&[0x01; 32]);
375 assert!(matches!(ssid, Ok(_)));
376 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<&[u8]>>::try_from(&[0x01; 33]);
377 assert!(matches!(ssid, Err(SsidError::Size(33))));
378 }
379
380 #[test]
381 fn ssid_index() {
382 assert_eq!(SSID_12345[0], 0x01);
383 assert_eq!(SSID_12345[1], 0x02);
384 assert_eq!(SSID_12345[2], 0x03);
385 assert_eq!(SSID_12345[3], 0x04);
386 assert_eq!(SSID_12345[4], 0x05);
387 }
388
389 #[test]
390 fn ssid_partial_eq_vec() {
391 assert_eq!(vec![], Ssid::empty());
392 assert_eq!(Ssid::empty(), vec![]);
393
394 assert_eq!(vec![1, 2, 3], Ssid::try_from([1, 2, 3]).unwrap());
395 assert_eq!(Ssid::try_from([1, 2, 3]).unwrap(), vec![1, 2, 3]);
396
397 assert_ne!(vec![1, 2], Ssid::try_from([1, 2, 3]).unwrap());
398 assert_ne!(Ssid::try_from([1, 2, 3]).unwrap(), vec![1, 2]);
399 }
400
401 #[test]
402 fn ssid_partial_eq_array() {
403 assert_eq!([], Ssid::empty());
404 assert_eq!(Ssid::empty(), []);
405
406 assert_eq!([1, 2, 3], Ssid::try_from([1, 2, 3]).unwrap());
407 assert_eq!(Ssid::try_from([1, 2, 3]).unwrap(), [1, 2, 3]);
408
409 assert_ne!([1, 2], Ssid::try_from([1, 2, 3]).unwrap());
410 assert_ne!(Ssid::try_from([1, 2, 3]).unwrap(), [1, 2]);
411 }
412
413 #[test]
414 fn ssid_partial_eq_slice() {
415 assert_eq!(&b""[..], &Ssid::empty());
416 assert_eq!(&Ssid::empty(), &b""[..]);
417
418 assert_eq!(&[1, 2, 3][..], &Ssid::try_from([1, 2, 3]).unwrap());
419 assert_eq!(&Ssid::try_from([1, 2, 3]).unwrap(), &[1, 2, 3][..]);
420
421 assert_ne!(&[1, 2][..], &Ssid::try_from([1, 2, 3]).unwrap());
422 assert_ne!(&Ssid::try_from([1, 2, 3]).unwrap(), &[1, 2][..]);
423 }
424
425 #[test]
426 fn ssid_to_string_not_redactable() {
427 assert_eq!(Ssid::empty().to_string_not_redactable(), "");
428
429 let sparkle_heart_ssid: Ssid = Ssid::try_from("💖").unwrap();
430 assert_eq!(sparkle_heart_ssid.to_string_not_redactable(), "💖");
431
432 let invalid_utf8_ssid: Ssid = Ssid::try_from([0x00, 0x9f, 0x92, 0x96]).unwrap();
433 assert_eq!(invalid_utf8_ssid.to_string_not_redactable(), "<ssid-009f9296>");
434 }
435
436 #[test]
437 fn ssid_empty() {
438 assert_eq!(Ssid::empty(), Ssid::try_from([]).unwrap());
439 assert_eq!(vec![], Ssid::empty());
440 assert_eq!(Ssid::empty().to_string(), "<ssid->");
441 assert_eq!(Ssid::empty().to_string_not_redactable(), "");
442 }
443
444 #[test]
445 fn ssid_len() {
446 assert_eq!(Ssid::empty().len(), 0);
447 assert_eq!(SSID_FOO.len(), 3);
448 assert_eq!(SSID_12345.len(), 5);
449 }
450
451 #[test]
452 fn ssid_to_vec() {
453 let ssid = SSID_12345.clone();
454 assert_eq!(ssid.to_vec(), vec![0x01, 0x02, 0x03, 0x04, 0x05]);
455 assert_eq!(<Ssid as Into<Vec<u8>>>::into(ssid), vec![0x01, 0x02, 0x03, 0x04, 0x05]);
457 }
458}