ascii_domain

Domains whose labels are only ASCII.
git clone https://git.philomathiclife.com/repos/ascii_domain
Log | Files | Refs | README

char_set.rs (23247B)


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