char_set.rs (22959B)
1 use core::{ 2 error::Error, 3 fmt::{self, Display, Formatter}, 4 str, 5 }; 6 /// Error returned from [`AllowedAscii::try_from_unique_ascii`]. 7 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] 8 pub enum AsciiErr { 9 /// Since `AllowedAscii` only allows unique ASCII characters and doesn't allow `b'.'`, the maximum count is 10 /// 127. This variant is returned when the count exceeds that. 11 CountTooLarge(usize), 12 /// The contained `u8` is not valid ASCII (i.e., it is strictly greater than 127). 13 InvalidByte(u8), 14 /// `b'.'` was in the allowed ASCII. It is the only ASCII value not allowed since it is always used 15 /// as a [`crate::dom::Label`] separator. 16 Contains46, 17 /// The contained ASCII appeared more than once. 18 Duplicate(u8), 19 } 20 impl Display for AsciiErr { 21 #[inline] 22 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 23 match *self { 24 Self::CountTooLarge(byt) => { 25 write!(f, "the allowed ASCII had {byt} values, but 127 is the max") 26 } 27 Self::InvalidByte(byt) => { 28 write!(f, "allowed ASCII was passed the invalid byte value {byt}") 29 } 30 Self::Contains46 => f.write_str("allowed ASCII contains '.'"), 31 Self::Duplicate(byt) => { 32 let input = [byt]; 33 if let Ok(val) = str::from_utf8(input.as_slice()) { 34 write!(f, "allowed ASCII has the duplicate value '{val}'") 35 } else { 36 write!(f, "allowed ASCII has the invalid value '{byt}'") 37 } 38 } 39 } 40 } 41 } 42 impl Error for AsciiErr {} 43 /// Container of the ASCII `u8`s that are allowed to appear in a [`crate::dom::Label`]. 44 /// 45 /// Note that while 46 /// [`crate::dom::Domain`] treats ASCII uppercase letters as lowercase, it still depends on such `u8`s being 47 /// included. For example if `b'A'` is not included, then `b'A'` is not allowed even if `b'a'` is included. 48 /// 49 /// It is _highly_ unlikely that non-printable ASCII nor `b'\\'` should be used since such ASCII would almost 50 /// certainly require being escaped. 51 pub struct AllowedAscii<T> { 52 /// The allowed ASCII `u8`s. 53 allowed: T, 54 } 55 impl<T> AllowedAscii<T> { 56 /// Returns a reference to the contained value. 57 /// 58 /// # Example 59 /// 60 /// ``` 61 /// use ascii_domain::char_set; 62 /// assert!(char_set::ASCII_LETTERS.as_inner().len() == 52); 63 /// ``` 64 #[inline] 65 pub const fn as_inner(&self) -> &T { 66 &self.allowed 67 } 68 /// Returns the contained value consuming `self`. 69 /// 70 /// # Example 71 /// 72 /// ``` 73 /// use ascii_domain::char_set; 74 /// assert!(char_set::ASCII_LETTERS.into_inner().len() == 52); 75 /// ``` 76 #[inline] 77 pub fn into_inner(self) -> T { 78 self.allowed 79 } 80 } 81 impl<T: AsRef<[u8]>> AllowedAscii<T> { 82 /// Returns `true` iff `val` is an allowed ASCII value in a [`crate::dom::Label`]. 83 /// 84 /// # Example 85 /// 86 /// ``` 87 /// use ascii_domain::char_set; 88 /// assert!(char_set::ASCII_LETTERS.contains(b'a')); 89 /// ``` 90 #[inline] 91 #[must_use] 92 pub fn contains(&self, val: u8) -> bool { 93 // We sort `allowed` in `try_from_unique_ascii`, so `binary_search` is fine. 94 self.allowed.as_ref().binary_search(&val).is_ok() 95 } 96 /// Returns the number of allowed ASCII characters. 97 /// 98 /// # Example 99 /// 100 /// ``` 101 /// use ascii_domain::char_set; 102 /// assert!(char_set::ASCII_LETTERS.len() == 52); 103 /// ``` 104 #[expect( 105 clippy::as_conversions, 106 clippy::cast_possible_truncation, 107 reason = "comment justifies its correctness" 108 )] 109 #[inline] 110 #[must_use] 111 pub fn len(&self) -> u8 { 112 // We enforce only unique non `b'.'` ASCII in `try_from_unique_ascii` which among other things means 113 // the max count is 127 so truncation will not occur 114 self.allowed.as_ref().len() as u8 115 } 116 /// Returns `true` iff `self` does not contain any ASCII. 117 /// 118 /// # Examples 119 /// 120 /// ``` 121 /// use ascii_domain::char_set::{self, AllowedAscii}; 122 /// assert!(!char_set::ASCII_LETTERS.is_empty()); 123 /// assert!(AllowedAscii::try_from_unique_ascii([]).unwrap().is_empty()); 124 /// ``` 125 #[inline] 126 #[must_use] 127 pub fn is_empty(&self) -> bool { 128 self.allowed.as_ref().is_empty() 129 } 130 } 131 impl<T: AsMut<[u8]>> AllowedAscii<T> { 132 /// `allowed` must contain unique ASCII `u8`s. Note it is likely `allowed` should be a subset of 133 /// [`PRINTABLE_ASCII`] since any other ASCII would likely require some form of escape character logic. 134 /// Additionally, it is likely `allowed.as_mut().len()` should be greater than 0; otherwise the returned 135 /// `AllowedAscii` will always cause [`crate::dom::Domain::try_from_bytes`] to error since `Domain` requires 136 /// at least one non-root [`crate::dom::Label`]. 137 /// 138 /// `allowed` is mutated such that `allowed.as_mut()` is sorted in order. 139 /// 140 /// # Errors 141 /// 142 /// Returns `AsciiError` iff `allowed` does not contain a set of unique ASCII `u8`s or contains `b'.'`. 143 /// 144 /// # Examples 145 /// 146 /// ``` 147 /// use ascii_domain::char_set::{AllowedAscii, AsciiErr}; 148 /// assert!(AllowedAscii::try_from_unique_ascii(b"asdfghjkl".to_owned()).map_or(false, |ascii| ascii.contains(b'a') && !ascii.contains(b'A'))); 149 /// assert!(AllowedAscii::try_from_unique_ascii(b"aa".to_owned()).map_or_else(|err| err == AsciiErr::Duplicate(b'a'), |_| false)); 150 /// assert!(AllowedAscii::try_from_unique_ascii([255]).map_or_else(|err| err == AsciiErr::InvalidByte(255), |_| false)); 151 /// assert!(AllowedAscii::try_from_unique_ascii([0; 128]).map_or_else(|err| err == AsciiErr::CountTooLarge(128), |_| false)); 152 /// assert!(AllowedAscii::try_from_unique_ascii([b'.']).map_or_else(|err| err == AsciiErr::Contains46, |_| false)); 153 /// ``` 154 #[inline] 155 pub fn try_from_unique_ascii(mut allowed: T) -> Result<Self, AsciiErr> { 156 let bytes = allowed.as_mut(); 157 if bytes.len() > 127 { 158 Err(AsciiErr::CountTooLarge(bytes.len())) 159 } else { 160 bytes.sort_unstable(); 161 // Since `bytes` is sorted, we simply have to check the last value to determine if valid ASCII was 162 // provided. 163 if let Some(byt) = bytes.last() { 164 let b = *byt; 165 if b > 127 { 166 return Err(AsciiErr::InvalidByte(b)); 167 } 168 } 169 bytes 170 .iter() 171 // 255 is not valid ASCII, so we can use it as an initializer. 172 .try_fold(255, |prev, b| { 173 let byt = *b; 174 if byt == b'.' { 175 Err(AsciiErr::Contains46) 176 } else if prev == byt { 177 Err(AsciiErr::Duplicate(prev)) 178 } else { 179 Ok(byt) 180 } 181 }) 182 .map(|_| Self { allowed }) 183 } 184 } 185 } 186 /// Printable ASCII that should not need to be "escaped". 187 /// 188 /// That is to say printable ASCII excluding space (i.e., 32), dot (i.e. 46), and backslash (i.e., 92). 189 /// This returns all `u8`s inclusively between 33 and 126 except 46 and 92. 190 pub const PRINTABLE_ASCII: AllowedAscii<[u8; 92]> = AllowedAscii { 191 allowed: [ 192 b'!', b'"', b'#', b'$', b'%', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'/', b'0', 193 b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', b'<', b'=', b'>', b'?', 194 b'@', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', 195 b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'[', b']', b'^', 196 b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', 197 b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', b'{', b'|', 198 b'}', b'~', 199 ], 200 }; 201 /// ASCII allowed in [RFC 5322 `atext`](https://www.rfc-editor.org/rfc/rfc5322#section-3.2.3). 202 /// This contains the following `u8`s: 203 /// 204 /// 33, 35–39, 42–43, 45, 47–57, 61, 63, 65–90, and 94–126. 205 pub const RFC5322_ATEXT: AllowedAscii<[u8; 81]> = AllowedAscii { 206 allowed: [ 207 b'!', b'#', b'$', b'%', b'&', b'\'', b'*', b'+', b'-', b'/', b'0', b'1', b'2', b'3', b'4', 208 b'5', b'6', b'7', b'8', b'9', b'=', b'?', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', 209 b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', 210 b'X', b'Y', b'Z', b'^', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', 211 b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', 212 b'y', b'z', b'{', b'|', b'}', b'~', 213 ], 214 }; 215 /// ASCII allowed in a domain by [Firefox](https://www.mozilla.org/en-US/firefox/) 216 /// as of 2023-09-03T20:50+00:00. 217 /// This contains the following `u8`s: 218 /// 219 /// 33, 36, 38–41, 43–45, 48–57, 59, 61, 65–90, 95–123, and 125–126. 220 pub const ASCII_FIREFOX: AllowedAscii<[u8; 78]> = AllowedAscii { 221 allowed: [ 222 b'!', b'$', b'&', b'\'', b'(', b')', b'+', b',', b'-', b'0', b'1', b'2', b'3', b'4', b'5', 223 b'6', b'7', b'8', b'9', b';', b'=', b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', 224 b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', 225 b'Y', b'Z', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', 226 b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', 227 b'{', b'}', b'~', 228 ], 229 }; 230 /// ASCII hyphen, digits, and letters. 231 /// This contains 45 and all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122. 232 pub const ASCII_HYPHEN_DIGITS_LETTERS: AllowedAscii<[u8; 63]> = AllowedAscii { 233 allowed: [ 234 b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', 235 b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', 236 b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', 237 b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', 238 b'x', b'y', b'z', 239 ], 240 }; 241 /// ASCII digits and letters. 242 /// This contains all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122. 243 pub const ASCII_DIGITS_LETTERS: AllowedAscii<[u8; 62]> = AllowedAscii { 244 allowed: [ 245 b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', 246 b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', 247 b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', 248 b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', 249 b'y', b'z', 250 ], 251 }; 252 /// ASCII letters. 253 /// This contains all `u8`s inclusively between 65 and 90 and 97 and 122. 254 pub const ASCII_LETTERS: AllowedAscii<[u8; 52]> = AllowedAscii { 255 allowed: [ 256 b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', 257 b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', 258 b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', 259 b't', b'u', b'v', b'w', b'x', b'y', b'z', 260 ], 261 }; 262 /// ASCII hyphen, digits, and uppercase letters. 263 /// This contains 45 and all `u8`s inclusively between 48 and 57 and 65 and 90. 264 pub const ASCII_HYPHEN_DIGITS_UPPERCASE: AllowedAscii<[u8; 37]> = AllowedAscii { 265 allowed: [ 266 b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', 267 b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', 268 b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', 269 ], 270 }; 271 /// ASCII hyphen, digits, and lowercase letters. 272 /// This contains 45 and all `u8`s inclusively between 48 and 57 and 97 and 122. 273 pub const ASCII_HYPHEN_DIGITS_LOWERCASE: AllowedAscii<[u8; 37]> = AllowedAscii { 274 allowed: [ 275 b'-', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', 276 b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', 277 b't', b'u', b'v', b'w', b'x', b'y', b'z', 278 ], 279 }; 280 /// ASCII digits and uppercase letters. 281 /// This contains all `u8`s inclusively between 48 and 57 and 65 and 90. 282 pub const ASCII_DIGITS_UPPERCASE: AllowedAscii<[u8; 36]> = AllowedAscii { 283 allowed: [ 284 b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'A', b'B', b'C', b'D', b'E', 285 b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', 286 b'U', b'V', b'W', b'X', b'Y', b'Z', 287 ], 288 }; 289 /// ASCII digits and lowercase letters. 290 /// This contains all `u8`s inclusively between 48 and 57 and 97 and 122. 291 pub const ASCII_DIGITS_LOWERCASE: AllowedAscii<[u8; 36]> = AllowedAscii { 292 allowed: [ 293 b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'a', b'b', b'c', b'd', b'e', 294 b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', 295 b'u', b'v', b'w', b'x', b'y', b'z', 296 ], 297 }; 298 /// ASCII uppercase letters. 299 /// This contains all `u8`s inclusively between 65 and 90. 300 pub const ASCII_UPPERCASE: AllowedAscii<[u8; 26]> = AllowedAscii { 301 allowed: [ 302 b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', 303 b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', 304 ], 305 }; 306 /// ASCII lowercase letters. 307 /// This contains all `u8`s inclusively between 97 and 122. 308 pub const ASCII_LOWERCASE: AllowedAscii<[u8; 26]> = AllowedAscii { 309 allowed: [ 310 b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', 311 b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', 312 ], 313 }; 314 /// ASCII digits. 315 /// This contains all `u8`s inclusively between 48 and 57. 316 pub const ASCII_DIGITS: AllowedAscii<[u8; 10]> = AllowedAscii { 317 allowed: [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9'], 318 }; 319 /// ASCII that is not a [forbidden domain code point](https://url.spec.whatwg.org/#forbidden-domain-code-point). 320 /// 321 /// This contains the following `u8`s: 322 /// 323 /// 33, 34, 36, 38–45, 48–57, 59, 61, 65–90, 95–123, and 125–126 324 pub const WHATWG_VALID_DOMAIN_CODE_POINTS: AllowedAscii<[u8; 80]> = AllowedAscii { 325 allowed: [ 326 b'!', b'"', b'$', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'0', b'1', b'2', b'3', 327 b'4', b'5', b'6', b'7', b'8', b'9', b';', b'=', b'A', b'B', b'C', b'D', b'E', b'F', b'G', 328 b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P', b'Q', b'R', b'S', b'T', b'U', b'V', 329 b'W', b'X', b'Y', b'Z', b'_', b'`', b'a', b'b', b'c', b'd', b'e', b'f', b'g', b'h', b'i', 330 b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', 331 b'y', b'z', b'{', b'}', b'~', 332 ], 333 }; 334 #[cfg(test)] 335 mod tests { 336 extern crate alloc; 337 use crate::char_set::{ 338 ASCII_DIGITS, ASCII_DIGITS_LETTERS, ASCII_DIGITS_LOWERCASE, ASCII_DIGITS_UPPERCASE, 339 ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS, ASCII_HYPHEN_DIGITS_LOWERCASE, 340 ASCII_HYPHEN_DIGITS_UPPERCASE, ASCII_LETTERS, ASCII_LOWERCASE, ASCII_UPPERCASE, 341 AllowedAscii, AsciiErr, PRINTABLE_ASCII, RFC5322_ATEXT, WHATWG_VALID_DOMAIN_CODE_POINTS, 342 }; 343 use alloc::{borrow::ToOwned, vec::Vec}; 344 #[test] 345 fn try_from() { 346 // Empty is allowed. 347 assert!(AllowedAscii::try_from_unique_ascii([]).is_ok()); 348 // Duplicates are not allowed. 349 assert!( 350 AllowedAscii::try_from_unique_ascii(b"aba".to_owned()) 351 .map_or_else(|e| e == AsciiErr::Duplicate(b'a'), |_| false) 352 ); 353 // `b'.'` is not allowed. 354 assert!( 355 AllowedAscii::try_from_unique_ascii(b"a.c".to_owned()) 356 .map_or_else(|e| e == AsciiErr::Contains46, |_| false) 357 ); 358 // At most 127 bytes are allowed. 359 assert!( 360 AllowedAscii::try_from_unique_ascii([0; 128]) 361 .map_or_else(|e| e == AsciiErr::CountTooLarge(128), |_| false) 362 ); 363 let mut all_ascii = (0..b'.').collect::<Vec<u8>>(); 364 let next = b'.' + 1; 365 all_ascii.extend(next..=127); 366 assert!(AllowedAscii::try_from_unique_ascii(all_ascii).is_ok()); 367 // Only ASCII is allowed. 368 assert!( 369 AllowedAscii::try_from_unique_ascii([255]) 370 .map_or_else(|e| e == AsciiErr::InvalidByte(255), |_| false) 371 ); 372 assert!( 373 AllowedAscii::try_from_unique_ascii(b"abcdef".to_owned()).map_or(false, |bytes| bytes 374 .contains(b'a') 375 && bytes.contains(b'b') 376 && bytes.contains(b'c') 377 && bytes.contains(b'd') 378 && bytes.contains(b'e') 379 && bytes.contains(b'f')) 380 ); 381 } 382 #[test] 383 fn test_consts() { 384 let letters = ASCII_LETTERS; 385 assert!(letters.len() == 52); 386 for i in b'A'..=b'Z' { 387 assert!(letters.contains(i)); 388 } 389 for i in b'a'..=b'z' { 390 assert!(letters.contains(i)); 391 } 392 let digits = ASCII_DIGITS; 393 assert!(digits.len() == 10); 394 for i in b'0'..=b'9' { 395 assert!(digits.contains(i)); 396 } 397 let lower = ASCII_LOWERCASE; 398 assert!(lower.len() == 26); 399 for i in b'a'..=b'z' { 400 assert!(lower.contains(i)); 401 } 402 let upper = ASCII_UPPERCASE; 403 assert!(upper.len() == 26); 404 for i in b'A'..=b'Z' { 405 assert!(upper.contains(i)); 406 } 407 let dig_let = ASCII_DIGITS_LETTERS; 408 assert!(dig_let.len() == 62); 409 for i in b'a'..=b'z' { 410 assert!(dig_let.contains(i)); 411 } 412 for i in b'0'..=b'9' { 413 assert!(dig_let.contains(i)); 414 } 415 for i in b'A'..=b'Z' { 416 assert!(dig_let.contains(i)); 417 } 418 let dig_lower = ASCII_DIGITS_LOWERCASE; 419 assert!(dig_lower.len() == 36); 420 for i in b'a'..=b'z' { 421 assert!(dig_lower.contains(i)); 422 } 423 for i in b'0'..=b'9' { 424 assert!(dig_lower.contains(i)); 425 } 426 let dig_upper = ASCII_DIGITS_UPPERCASE; 427 assert!(dig_upper.len() == 36); 428 for i in b'A'..=b'Z' { 429 assert!(dig_upper.contains(i)); 430 } 431 for i in b'0'..=b'9' { 432 assert!(dig_upper.contains(i)); 433 } 434 let ffox = ASCII_FIREFOX; 435 assert!(ffox.len() == 78); 436 for i in b'A'..=b'Z' { 437 assert!(ffox.contains(i)); 438 } 439 for i in b'a'..=b'z' { 440 assert!(ffox.contains(i)); 441 } 442 for i in b'0'..=b'9' { 443 assert!(ffox.contains(i)); 444 } 445 assert!(ffox.contains(b'!')); 446 assert!(ffox.contains(b'$')); 447 assert!(ffox.contains(b'&')); 448 assert!(ffox.contains(b'\'')); 449 assert!(ffox.contains(b'(')); 450 assert!(ffox.contains(b')')); 451 assert!(ffox.contains(b'+')); 452 assert!(ffox.contains(b',')); 453 assert!(ffox.contains(b'-')); 454 assert!(ffox.contains(b';')); 455 assert!(ffox.contains(b'=')); 456 assert!(ffox.contains(b'_')); 457 assert!(ffox.contains(b'`')); 458 assert!(ffox.contains(b'{')); 459 assert!(ffox.contains(b'}')); 460 assert!(ffox.contains(b'~')); 461 assert!(ASCII_HYPHEN_DIGITS_LETTERS.len() == 63); 462 assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(b'-')); 463 for i in b'A'..=b'Z' { 464 assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); 465 } 466 for i in b'a'..=b'z' { 467 assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); 468 } 469 for i in b'0'..=b'9' { 470 assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); 471 } 472 let hyp_lower = ASCII_HYPHEN_DIGITS_LOWERCASE; 473 assert!(hyp_lower.len() == 37); 474 assert!(hyp_lower.contains(b'-')); 475 for i in b'a'..=b'z' { 476 assert!(hyp_lower.contains(i)); 477 } 478 for i in b'0'..=b'9' { 479 assert!(hyp_lower.contains(i)); 480 } 481 let hyp_upper = ASCII_HYPHEN_DIGITS_UPPERCASE; 482 assert!(hyp_upper.len() == 37); 483 assert!(hyp_upper.contains(b'-')); 484 for i in b'A'..=b'Z' { 485 assert!(hyp_upper.contains(i)); 486 } 487 for i in b'0'..=b'9' { 488 assert!(hyp_upper.contains(i)); 489 } 490 let printable = PRINTABLE_ASCII; 491 assert!(printable.len() == 92); 492 let stop = b'.' - 1; 493 for i in 33..=stop { 494 assert!(printable.contains(i)); 495 } 496 let stop2 = b'\\' - 1; 497 for i in stop + 2..=stop2 { 498 assert!(printable.contains(i)); 499 } 500 for i in stop2 + 2..=b'~' { 501 assert!(printable.contains(i)); 502 } 503 let rfc = RFC5322_ATEXT; 504 assert!(rfc.len() == 81); 505 for i in b'A'..=b'Z' { 506 assert!(rfc.contains(i)); 507 } 508 for i in b'a'..=b'z' { 509 assert!(rfc.contains(i)); 510 } 511 for i in b'0'..=b'9' { 512 assert!(rfc.contains(i)); 513 } 514 assert!(rfc.contains(b'!')); 515 assert!(rfc.contains(b'#')); 516 assert!(rfc.contains(b'$')); 517 assert!(rfc.contains(b'%')); 518 assert!(rfc.contains(b'&')); 519 assert!(rfc.contains(b'\'')); 520 assert!(rfc.contains(b'*')); 521 assert!(rfc.contains(b'+')); 522 assert!(rfc.contains(b'-')); 523 assert!(rfc.contains(b'/')); 524 assert!(rfc.contains(b'=')); 525 assert!(rfc.contains(b'?')); 526 assert!(rfc.contains(b'^')); 527 assert!(rfc.contains(b'_')); 528 assert!(rfc.contains(b'`')); 529 assert!(rfc.contains(b'{')); 530 assert!(rfc.contains(b'|')); 531 assert!(rfc.contains(b'}')); 532 assert!(rfc.contains(b'~')); 533 let whatwg = WHATWG_VALID_DOMAIN_CODE_POINTS; 534 for i in 0..=0x1f { 535 assert!(!whatwg.contains(i)); 536 } 537 assert!(!whatwg.contains(b'\x20')); 538 assert!(!whatwg.contains(b'#')); 539 assert!(!whatwg.contains(b'/')); 540 assert!(!whatwg.contains(b':')); 541 assert!(!whatwg.contains(b'<')); 542 assert!(!whatwg.contains(b'>')); 543 assert!(!whatwg.contains(b'?')); 544 assert!(!whatwg.contains(b'@')); 545 assert!(!whatwg.contains(b'[')); 546 assert!(!whatwg.contains(b'\\')); 547 assert!(!whatwg.contains(b']')); 548 assert!(!whatwg.contains(b'^')); 549 assert!(!whatwg.contains(b'|')); 550 assert!(!whatwg.contains(b'%')); 551 assert!(!whatwg.contains(b'\x7f')); 552 assert!(!whatwg.contains(b'.')); 553 assert!(whatwg.len() == 128 - 32 - 16); 554 } 555 }