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 lazy_static::lazy_static;
239
240 lazy_static! {
241 static ref SSID_12345: Ssid = Ssid::try_from([0x01, 0x02, 0x03, 0x04, 0x05]).unwrap();
242 static ref SSID_FOO: Ssid = Ssid::try_from([0x66, 0x6F, 0x6F]).unwrap();
243 }
244
245 #[test]
246 fn ssid_check() {
247 assert_eq!(Ok(()), Ssid::check(&[0x2; 0]));
248 assert_eq!(Ok(()), Ssid::check(&[0x2; 20]));
249 assert_eq!(Ok(()), Ssid::check(&[0x2; 32]));
250 assert_eq!(Err(SsidError::Size(33)), Ssid::check(&[0x2; 33]));
251 }
252
253 #[test]
254 fn ssid_as_ref() {
255 let array_12345: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05];
256 assert_eq!((*SSID_12345).as_ref(), array_12345.as_ref());
257 }
258
259 #[test]
260 fn ssid_deref() {
261 let array_12345: [u8; 5] = [0x01, 0x02, 0x03, 0x04, 0x05];
262 assert_eq!(**SSID_12345, array_12345);
263 }
264
265 #[test]
266 fn ssid_to_string() {
267 assert_eq!(Ssid::empty().to_string(), "<ssid->");
268 assert_eq!(SSID_FOO.to_string(), "<ssid-666f6f>");
269 assert_eq!(SSID_12345.to_string(), "<ssid-0102030405>");
270 }
271
272 #[test]
273 fn format_ssid() {
274 assert_eq!(format!("{}", *SSID_12345), "<ssid-0102030405>");
275 }
276
277 #[test]
278 fn ssid_into_vec() {
279 assert_eq!(
280 <Ssid as Into<Vec<u8>>>::into(SSID_12345.clone()),
281 vec![0x01, 0x02, 0x03, 0x04, 0x05]
282 );
283 }
284
285 #[test]
286 fn ssid_try_from_boxed_slice_ok() {
287 let ssid: Ssid =
288 <Ssid as TryFrom<Box<[u8]>>>::try_from(Box::from([0x01, 0x02, 0x03, 0x04, 0x05]))
289 .expect("Failed to convert Box<[u8]> to Ssid");
290 assert_eq!(ssid, *SSID_12345);
291 }
292
293 #[test]
294 fn ssid_try_from_array_ok() {
295 let array = [0x01, 0x02, 0x03, 0x04, 0x05];
296 let ssid: Ssid =
297 <Ssid as TryFrom<[u8; 5]>>::try_from(array).expect("Failed to convert [u8; 5] to Ssid");
298 assert_eq!(ssid, *SSID_12345);
299 }
300
301 #[test]
302 fn ssid_try_from_boxed_str_ok() {
303 let ssid: Ssid =
304 <Ssid as TryFrom<Box<str>>>::try_from(String::from("foo").into_boxed_str())
305 .expect("Failed to convert Box<str> to Ssid");
306 assert_eq!(ssid, *SSID_FOO);
307 }
308
309 #[test]
310 fn ssid_try_from_string_ok() {
311 let ssid: Ssid = <Ssid as TryFrom<String>>::try_from(String::from("foo"))
312 .expect("Failed to convert String to Ssid");
313 assert_eq!(ssid, *SSID_FOO);
314 }
315
316 #[test]
317 fn ssid_try_from_vec_ok() {
318 let ssid: Ssid = <Ssid as TryFrom<Vec<u8>>>::try_from(vec![0x01, 0x02, 0x03, 0x04, 0x05])
319 .expect("Failed to convert Vec<u8> to Ssid");
320 assert_eq!(ssid, *SSID_12345);
321 }
322
323 #[test]
324 fn ssid_try_from_str_ok() {
325 let ssid: Ssid =
326 <Ssid as TryFrom<&str>>::try_from("foo").expect("Failed to convert &str to Ssid");
327 assert_eq!(ssid, *SSID_FOO);
328 }
329
330 #[test]
331 fn ssid_try_from_slice_ok() {
332 let ssid: Ssid = <Ssid as TryFrom<&[u8]>>::try_from(&[0x01, 0x02, 0x03, 0x04, 0x05])
333 .expect("Failed to convert &[u8] to Ssid");
334 assert_eq!(ssid, *SSID_12345);
335 }
336
337 #[test]
338 fn ssid_try_from_array_err() {
339 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<[u8; 32]>>::try_from([0x03; 32]);
340 assert!(matches!(ssid, Ok(_)));
341 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<[u8; 50]>>::try_from([0x03; 50]);
342 assert!(matches!(ssid, Err(SsidError::Size(50))));
343 }
344
345 #[test]
346 fn ssid_try_from_string_err() {
347 let ssid: Result<Ssid, SsidError> =
348 <Ssid as TryFrom<String>>::try_from(String::from("12345678901234567890123456789012"));
349 assert!(matches!(ssid, Ok(_)));
350 let ssid: Result<Ssid, SsidError> =
351 <Ssid as TryFrom<String>>::try_from(String::from("123456789012345678901234567890123"));
352 assert!(matches!(ssid, Err(SsidError::Size(33))));
353 }
354
355 #[test]
356 fn ssid_try_from_vec_err() {
357 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<Vec<u8>>>::try_from(vec![0x07; 32]);
358 assert!(matches!(ssid, Ok(_)));
359 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<Vec<u8>>>::try_from(vec![0x07; 100]);
360 assert!(matches!(ssid, Err(SsidError::Size(100))));
361 }
362
363 #[test]
364 fn ssid_try_from_str_err() {
365 let ssid: Result<Ssid, SsidError> =
366 <Ssid as TryFrom<&str>>::try_from("12345678901234567890123456789012");
367 assert!(matches!(ssid, Ok(_)));
368 let ssid: Result<Ssid, SsidError> =
369 <Ssid as TryFrom<&str>>::try_from("123456789012345678901234567890123");
370 assert!(matches!(ssid, Err(SsidError::Size(33))));
371 }
372
373 #[test]
374 fn ssid_try_from_slice_err() {
375 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<&[u8]>>::try_from(&[0x01; 32]);
376 assert!(matches!(ssid, Ok(_)));
377 let ssid: Result<Ssid, SsidError> = <Ssid as TryFrom<&[u8]>>::try_from(&[0x01; 33]);
378 assert!(matches!(ssid, Err(SsidError::Size(33))));
379 }
380
381 #[test]
382 fn ssid_index() {
383 assert_eq!(SSID_12345[0], 0x01);
384 assert_eq!(SSID_12345[1], 0x02);
385 assert_eq!(SSID_12345[2], 0x03);
386 assert_eq!(SSID_12345[3], 0x04);
387 assert_eq!(SSID_12345[4], 0x05);
388 }
389
390 #[test]
391 fn ssid_partial_eq_vec() {
392 assert_eq!(vec![], Ssid::empty());
393 assert_eq!(Ssid::empty(), vec![]);
394
395 assert_eq!(vec![1, 2, 3], Ssid::try_from([1, 2, 3]).unwrap());
396 assert_eq!(Ssid::try_from([1, 2, 3]).unwrap(), vec![1, 2, 3]);
397
398 assert_ne!(vec![1, 2], Ssid::try_from([1, 2, 3]).unwrap());
399 assert_ne!(Ssid::try_from([1, 2, 3]).unwrap(), vec![1, 2]);
400 }
401
402 #[test]
403 fn ssid_partial_eq_array() {
404 assert_eq!([], Ssid::empty());
405 assert_eq!(Ssid::empty(), []);
406
407 assert_eq!([1, 2, 3], Ssid::try_from([1, 2, 3]).unwrap());
408 assert_eq!(Ssid::try_from([1, 2, 3]).unwrap(), [1, 2, 3]);
409
410 assert_ne!([1, 2], Ssid::try_from([1, 2, 3]).unwrap());
411 assert_ne!(Ssid::try_from([1, 2, 3]).unwrap(), [1, 2]);
412 }
413
414 #[test]
415 fn ssid_partial_eq_slice() {
416 assert_eq!(&b""[..], &Ssid::empty());
417 assert_eq!(&Ssid::empty(), &b""[..]);
418
419 assert_eq!(&[1, 2, 3][..], &Ssid::try_from([1, 2, 3]).unwrap());
420 assert_eq!(&Ssid::try_from([1, 2, 3]).unwrap(), &[1, 2, 3][..]);
421
422 assert_ne!(&[1, 2][..], &Ssid::try_from([1, 2, 3]).unwrap());
423 assert_ne!(&Ssid::try_from([1, 2, 3]).unwrap(), &[1, 2][..]);
424 }
425
426 #[test]
427 fn ssid_to_string_not_redactable() {
428 assert_eq!(Ssid::empty().to_string_not_redactable(), "");
429
430 let sparkle_heart_ssid: Ssid = Ssid::try_from("💖").unwrap();
431 assert_eq!(sparkle_heart_ssid.to_string_not_redactable(), "💖");
432
433 let invalid_utf8_ssid: Ssid = Ssid::try_from([0x00, 0x9f, 0x92, 0x96]).unwrap();
434 assert_eq!(invalid_utf8_ssid.to_string_not_redactable(), "<ssid-009f9296>");
435 }
436
437 #[test]
438 fn ssid_empty() {
439 assert_eq!(Ssid::empty(), Ssid::try_from([]).unwrap());
440 assert_eq!(vec![], Ssid::empty());
441 assert_eq!(Ssid::empty().to_string(), "<ssid->");
442 assert_eq!(Ssid::empty().to_string_not_redactable(), "");
443 }
444
445 #[test]
446 fn ssid_len() {
447 assert_eq!(Ssid::empty().len(), 0);
448 assert_eq!(SSID_FOO.len(), 3);
449 assert_eq!(SSID_12345.len(), 5);
450 }
451
452 #[test]
453 fn ssid_to_vec() {
454 let ssid = SSID_12345.clone();
455 assert_eq!(ssid.to_vec(), vec![0x01, 0x02, 0x03, 0x04, 0x05]);
456 assert_eq!(<Ssid as Into<Vec<u8>>>::into(ssid), vec![0x01, 0x02, 0x03, 0x04, 0x05]);
458 }
459}