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