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