ascii_domain

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

commit d756babec5110b22456f56daf3348d610ebd96ed
parent 5b809542b209411b55ce924d058dd9cc4e97d207
Author: Zack Newman <zack@philomathiclife.com>
Date:   Mon, 13 Jul 2026 11:53:21 -0600

bump msrv. new lints. move unit tests to own file

Diffstat:
MCargo.toml | 12++++++++----
Msrc/char_set.rs | 332++++---------------------------------------------------------------------------
Asrc/char_set/tests.rs | 231+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/dom.rs | 341+------------------------------------------------------------------------------
Asrc/dom/tests.rs | 357+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/serde.rs | 51+++------------------------------------------------
Asrc/serde/tests.rs | 45+++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 664 insertions(+), 705 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml @@ -9,8 +9,8 @@ license = "MIT OR Apache-2.0" name = "ascii_domain" readme = "README.md" repository = "https://git.philomathiclife.com/repos/ascii_domain/" -rust-version = "1.93.1" -version = "0.6.9" +rust-version = "1.97.0" +version = "0.6.10" [lints.rust] deprecated-safe = { level = "deny", priority = -1 } @@ -28,12 +28,14 @@ unused = { level = "deny", priority = -1 } warnings = { level = "deny", priority = -1 } ambiguous-negative-literals = { level = "deny", priority = -1 } closure-returning-async-block = { level = "deny", priority = -1 } +dead-code-pub-in-binary = { level = "deny", priority = -1 } deprecated-in-future = { level = "deny", priority = -1 } +#deprecated-llvm-intrinsic = { level = "deny", priority = -1 } deref-into-dyn-supertrait = { level = "deny", priority = -1 } ffi-unwind-calls = { level = "deny", priority = -1 } #fuzzy-provenance-casts = { level = "deny", priority = -1 } impl-trait-redundant-captures = { level = "deny", priority = -1 } -linker-messages = { level = "deny", priority = -1 } +linker-info = { level = "deny", priority = -1 } #lossy-provenance-casts = { level = "deny", priority = -1 } macro-use-extern-crate = { level = "deny", priority = -1 } meta-variable-misuse = { level = "deny", priority = -1 } @@ -79,10 +81,12 @@ arbitrary_source_item_ordering = "allow" blanket_clippy_restriction_lints = "allow" exhaustive_enums = "allow" implicit_return = "allow" +inline_trait_bounds = "allow" min_ident_chars = "allow" missing_trait_methods = "allow" question_mark_used = "allow" return_and_then = "allow" +self_named_module_files = "allow" single_char_lifetime_names = "allow" unseparated_literal_suffix = "allow" @@ -109,7 +113,7 @@ targets = [ serde = { version = "1.0.228", default-features = false, features = ["alloc"], optional = true } [dev-dependencies] -serde_json = { version = "1.0.149", default-features = false, features = ["alloc"] } +serde_json = { version = "1.0.150", default-features = false, features = ["alloc"] } ### FEATURES ################################################################# diff --git a/src/char_set.rs b/src/char_set.rs @@ -1,3 +1,6 @@ +#[cfg(test)] +/// Unit tests. +mod tests; use core::{ error::Error, fmt::{self, Display, Formatter}, @@ -191,29 +194,14 @@ impl<T: AsMut<[u8]>> AllowedAscii<T> { /// That is to say printable ASCII excluding space (i.e., 32), dot (i.e. 46), and backslash (i.e., 92). /// This returns all `u8`s inclusively between 33 and 126 except 46 and 92. pub const PRINTABLE_ASCII: AllowedAscii<[u8; 92]> = AllowedAscii { - allowed: [ - b'!', b'"', b'#', b'$', b'%', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'/', b'0', - b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', b';', b'<', b'=', b'>', 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', 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']', b'^', - 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', - 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'|', - b'}', b'~', - ], + allowed: *b"!\"#$%&'()*+,-/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~", }; /// ASCII allowed in [RFC 5322 `atext`](https://www.rfc-editor.org/rfc/rfc5322#section-3.2.3). /// This contains the following `u8`s: /// /// 33, 35–39, 42–43, 45, 47–57, 61, 63, 65–90, and 94–126. pub const RFC5322_ATEXT: AllowedAscii<[u8; 81]> = AllowedAscii { - allowed: [ - b'!', b'#', b'$', b'%', b'&', b'\'', b'*', b'+', b'-', b'/', b'0', b'1', b'2', b'3', 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', 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', b'W', - 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', - 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', - b'y', b'z', b'{', b'|', b'}', b'~', - ], + allowed: *b"!#$%&'*+-/0123456789=?ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~", }; /// ASCII allowed in a domain by [Firefox](https://www.mozilla.org/en-US/firefox/) /// as of 2023-09-03T20:50+00:00. @@ -221,103 +209,57 @@ pub const RFC5322_ATEXT: AllowedAscii<[u8; 81]> = AllowedAscii { /// /// 33, 36, 38–41, 43–45, 48–57, 59, 61, 65–90, 95–123, and 125–126. pub const ASCII_FIREFOX: AllowedAscii<[u8; 78]> = AllowedAscii { - allowed: [ - b'!', b'$', b'&', b'\'', b'(', b')', b'+', b',', b'-', b'0', b'1', b'2', b'3', 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', 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', 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', 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', b'y', b'z', - b'{', b'}', b'~', - ], + allowed: *b"!$&'()+,-0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz{}~", }; /// ASCII hyphen, digits, and letters. /// This contains 45 and all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122. pub const ASCII_HYPHEN_DIGITS_LETTERS: AllowedAscii<[u8; 63]> = AllowedAscii { - allowed: [ - 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', - 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', - 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', - 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', - b'x', b'y', b'z', - ], + allowed: *b"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", }; /// ASCII digits and letters. /// This contains all `u8`s inclusively between 48 and 57, 65 and 90, and 97 and 122. pub const ASCII_DIGITS_LETTERS: AllowedAscii<[u8; 62]> = AllowedAscii { - allowed: [ - 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', - 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', - 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', - 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', - b'y', b'z', - ], + allowed: *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", }; /// ASCII letters. /// This contains all `u8`s inclusively between 65 and 90 and 97 and 122. pub const ASCII_LETTERS: AllowedAscii<[u8; 52]> = AllowedAscii { - allowed: [ - 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', - 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', - 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', - b't', b'u', b'v', b'w', b'x', b'y', b'z', - ], + allowed: *b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", }; /// ASCII hyphen, digits, and uppercase letters. /// This contains 45 and all `u8`s inclusively between 48 and 57 and 65 and 90. pub const ASCII_HYPHEN_DIGITS_UPPERCASE: AllowedAscii<[u8; 37]> = AllowedAscii { - allowed: [ - 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', - 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', - b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', - ], + allowed: *b"-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", }; /// ASCII hyphen, digits, and lowercase letters. /// This contains 45 and all `u8`s inclusively between 48 and 57 and 97 and 122. pub const ASCII_HYPHEN_DIGITS_LOWERCASE: AllowedAscii<[u8; 37]> = AllowedAscii { - allowed: [ - 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', - 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', - b't', b'u', b'v', b'w', b'x', b'y', b'z', - ], + allowed: *b"-0123456789abcdefghijklmnopqrstuvwxyz", }; /// ASCII digits and uppercase letters. /// This contains all `u8`s inclusively between 48 and 57 and 65 and 90. pub const ASCII_DIGITS_UPPERCASE: AllowedAscii<[u8; 36]> = AllowedAscii { - allowed: [ - 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', - 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', - b'U', b'V', b'W', b'X', b'Y', b'Z', - ], + allowed: *b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ", }; /// ASCII digits and lowercase letters. /// This contains all `u8`s inclusively between 48 and 57 and 97 and 122. pub const ASCII_DIGITS_LOWERCASE: AllowedAscii<[u8; 36]> = AllowedAscii { - allowed: [ - 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', - 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', - b'u', b'v', b'w', b'x', b'y', b'z', - ], + allowed: *b"0123456789abcdefghijklmnopqrstuvwxyz", }; /// ASCII uppercase letters. /// This contains all `u8`s inclusively between 65 and 90. pub const ASCII_UPPERCASE: AllowedAscii<[u8; 26]> = AllowedAscii { - allowed: [ - 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', - b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', - ], + allowed: *b"ABCDEFGHIJKLMNOPQRSTUVWXYZ", }; /// ASCII lowercase letters. /// This contains all `u8`s inclusively between 97 and 122. pub const ASCII_LOWERCASE: AllowedAscii<[u8; 26]> = AllowedAscii { - allowed: [ - 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', - b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', b'x', b'y', b'z', - ], + allowed: *b"abcdefghijklmnopqrstuvwxyz", }; /// ASCII digits. /// This contains all `u8`s inclusively between 48 and 57. pub const ASCII_DIGITS: AllowedAscii<[u8; 10]> = AllowedAscii { - allowed: [b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9'], + allowed: *b"0123456789", }; /// ASCII that is not a [forbidden domain code point](https://url.spec.whatwg.org/#forbidden-domain-code-point). /// @@ -325,245 +267,5 @@ pub const ASCII_DIGITS: AllowedAscii<[u8; 10]> = AllowedAscii { /// /// 33, 34, 36, 38–45, 48–57, 59, 61, 65–90, 95–123, and 125–126. pub const WHATWG_VALID_DOMAIN_CODE_POINTS: AllowedAscii<[u8; 80]> = AllowedAscii { - allowed: [ - b'!', b'"', b'$', b'&', b'\'', b'(', b')', b'*', b'+', b',', b'-', b'0', b'1', b'2', b'3', - 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', - 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', - 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', - 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', - b'y', b'z', b'{', b'}', b'~', - ], + allowed: *b"!\"$&'()*+,-0123456789;=ABCDEFGHIJKLMNOPQRSTUVWXYZ_`abcdefghijklmnopqrstuvwxyz{}~", }; -#[cfg(test)] -mod tests { - extern crate alloc; - use crate::char_set::{ - ASCII_DIGITS, ASCII_DIGITS_LETTERS, ASCII_DIGITS_LOWERCASE, ASCII_DIGITS_UPPERCASE, - ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS, ASCII_HYPHEN_DIGITS_LOWERCASE, - ASCII_HYPHEN_DIGITS_UPPERCASE, ASCII_LETTERS, ASCII_LOWERCASE, ASCII_UPPERCASE, - AllowedAscii, AsciiErr, PRINTABLE_ASCII, RFC5322_ATEXT, WHATWG_VALID_DOMAIN_CODE_POINTS, - }; - use alloc::{borrow::ToOwned as _, vec::Vec}; - #[test] - fn try_from() { - // Empty is allowed. - assert_eq!( - AllowedAscii::try_from_unique_ascii([]), - Ok(AllowedAscii { allowed: [] }) - ); - // Duplicates are not allowed. - assert_eq!( - AllowedAscii::try_from_unique_ascii(b"aba".to_owned()), - Err(AsciiErr::Duplicate(b'a')) - ); - // `b'.'` is not allowed. - assert_eq!( - AllowedAscii::try_from_unique_ascii(b"a.c".to_owned()), - Err(AsciiErr::Contains46) - ); - // At most 127 bytes are allowed. - assert_eq!( - AllowedAscii::try_from_unique_ascii([0; 128]), - Err(AsciiErr::CountTooLarge(128)) - ); - let mut all_ascii = (0..b'.').collect::<Vec<u8>>(); - let next = b'.' + 1; - all_ascii.extend(next..=127); - assert_eq!( - AllowedAscii::try_from_unique_ascii(all_ascii.clone()), - Ok(AllowedAscii { allowed: all_ascii }) - ); - // Only ASCII is allowed. - assert_eq!( - AllowedAscii::try_from_unique_ascii([255]), - Err(AsciiErr::InvalidByte(255)) - ); - assert!( - AllowedAscii::try_from_unique_ascii(b"abcdef".to_owned()).is_ok_and(|bytes| bytes - .contains(b'a') - && bytes.contains(b'b') - && bytes.contains(b'c') - && bytes.contains(b'd') - && bytes.contains(b'e') - && bytes.contains(b'f')) - ); - } - #[expect( - clippy::cognitive_complexity, - clippy::too_many_lines, - reason = "want to test a lot of things" - )] - #[test] - fn consts() { - let letters = ASCII_LETTERS; - assert!(letters.len() == 52); - for i in b'A'..=b'Z' { - assert!(letters.contains(i)); - } - for i in b'a'..=b'z' { - assert!(letters.contains(i)); - } - let digits = ASCII_DIGITS; - assert!(digits.len() == 10); - for i in b'0'..=b'9' { - assert!(digits.contains(i)); - } - let lower = ASCII_LOWERCASE; - assert!(lower.len() == 26); - for i in b'a'..=b'z' { - assert!(lower.contains(i)); - } - let upper = ASCII_UPPERCASE; - assert!(upper.len() == 26); - for i in b'A'..=b'Z' { - assert!(upper.contains(i)); - } - let dig_let = ASCII_DIGITS_LETTERS; - assert!(dig_let.len() == 62); - for i in b'a'..=b'z' { - assert!(dig_let.contains(i)); - } - for i in b'0'..=b'9' { - assert!(dig_let.contains(i)); - } - for i in b'A'..=b'Z' { - assert!(dig_let.contains(i)); - } - let dig_lower = ASCII_DIGITS_LOWERCASE; - assert!(dig_lower.len() == 36); - for i in b'a'..=b'z' { - assert!(dig_lower.contains(i)); - } - for i in b'0'..=b'9' { - assert!(dig_lower.contains(i)); - } - let dig_upper = ASCII_DIGITS_UPPERCASE; - assert!(dig_upper.len() == 36); - for i in b'A'..=b'Z' { - assert!(dig_upper.contains(i)); - } - for i in b'0'..=b'9' { - assert!(dig_upper.contains(i)); - } - let ffox = ASCII_FIREFOX; - assert!(ffox.len() == 78); - for i in b'A'..=b'Z' { - assert!(ffox.contains(i)); - } - for i in b'a'..=b'z' { - assert!(ffox.contains(i)); - } - for i in b'0'..=b'9' { - assert!(ffox.contains(i)); - } - assert!(ffox.contains(b'!')); - assert!(ffox.contains(b'$')); - assert!(ffox.contains(b'&')); - assert!(ffox.contains(b'\'')); - assert!(ffox.contains(b'(')); - assert!(ffox.contains(b')')); - assert!(ffox.contains(b'+')); - assert!(ffox.contains(b',')); - assert!(ffox.contains(b'-')); - assert!(ffox.contains(b';')); - assert!(ffox.contains(b'=')); - assert!(ffox.contains(b'_')); - assert!(ffox.contains(b'`')); - assert!(ffox.contains(b'{')); - assert!(ffox.contains(b'}')); - assert!(ffox.contains(b'~')); - assert!(ASCII_HYPHEN_DIGITS_LETTERS.len() == 63); - assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(b'-')); - for i in b'A'..=b'Z' { - assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); - } - for i in b'a'..=b'z' { - assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); - } - for i in b'0'..=b'9' { - assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); - } - let hyp_lower = ASCII_HYPHEN_DIGITS_LOWERCASE; - assert!(hyp_lower.len() == 37); - assert!(hyp_lower.contains(b'-')); - for i in b'a'..=b'z' { - assert!(hyp_lower.contains(i)); - } - for i in b'0'..=b'9' { - assert!(hyp_lower.contains(i)); - } - let hyp_upper = ASCII_HYPHEN_DIGITS_UPPERCASE; - assert!(hyp_upper.len() == 37); - assert!(hyp_upper.contains(b'-')); - for i in b'A'..=b'Z' { - assert!(hyp_upper.contains(i)); - } - for i in b'0'..=b'9' { - assert!(hyp_upper.contains(i)); - } - let printable = PRINTABLE_ASCII; - assert!(printable.len() == 92); - let stop = b'.' - 1; - for i in 33..=stop { - assert!(printable.contains(i)); - } - let stop2 = b'\\' - 1; - for i in stop + 2..=stop2 { - assert!(printable.contains(i)); - } - for i in stop2 + 2..=b'~' { - assert!(printable.contains(i)); - } - let rfc = RFC5322_ATEXT; - assert!(rfc.len() == 81); - for i in b'A'..=b'Z' { - assert!(rfc.contains(i)); - } - for i in b'a'..=b'z' { - assert!(rfc.contains(i)); - } - for i in b'0'..=b'9' { - assert!(rfc.contains(i)); - } - assert!(rfc.contains(b'!')); - assert!(rfc.contains(b'#')); - assert!(rfc.contains(b'$')); - assert!(rfc.contains(b'%')); - assert!(rfc.contains(b'&')); - assert!(rfc.contains(b'\'')); - assert!(rfc.contains(b'*')); - assert!(rfc.contains(b'+')); - assert!(rfc.contains(b'-')); - assert!(rfc.contains(b'/')); - assert!(rfc.contains(b'=')); - assert!(rfc.contains(b'?')); - assert!(rfc.contains(b'^')); - assert!(rfc.contains(b'_')); - assert!(rfc.contains(b'`')); - assert!(rfc.contains(b'{')); - assert!(rfc.contains(b'|')); - assert!(rfc.contains(b'}')); - assert!(rfc.contains(b'~')); - let whatwg = WHATWG_VALID_DOMAIN_CODE_POINTS; - for i in 0..=0x1f { - assert!(!whatwg.contains(i)); - } - assert!(!whatwg.contains(b'\x20')); - assert!(!whatwg.contains(b'#')); - assert!(!whatwg.contains(b'/')); - assert!(!whatwg.contains(b':')); - assert!(!whatwg.contains(b'<')); - assert!(!whatwg.contains(b'>')); - assert!(!whatwg.contains(b'?')); - assert!(!whatwg.contains(b'@')); - assert!(!whatwg.contains(b'[')); - assert!(!whatwg.contains(b'\\')); - assert!(!whatwg.contains(b']')); - assert!(!whatwg.contains(b'^')); - assert!(!whatwg.contains(b'|')); - assert!(!whatwg.contains(b'%')); - assert!(!whatwg.contains(b'\x7f')); - assert!(!whatwg.contains(b'.')); - assert!(whatwg.len() == 128 - 32 - 16); - } -} diff --git a/src/char_set/tests.rs b/src/char_set/tests.rs @@ -0,0 +1,231 @@ +extern crate alloc; +use super::{ + ASCII_DIGITS, ASCII_DIGITS_LETTERS, ASCII_DIGITS_LOWERCASE, ASCII_DIGITS_UPPERCASE, + ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS, ASCII_HYPHEN_DIGITS_LOWERCASE, + ASCII_HYPHEN_DIGITS_UPPERCASE, ASCII_LETTERS, ASCII_LOWERCASE, ASCII_UPPERCASE, AllowedAscii, + AsciiErr, PRINTABLE_ASCII, RFC5322_ATEXT, WHATWG_VALID_DOMAIN_CODE_POINTS, +}; +use alloc::{borrow::ToOwned as _, vec::Vec}; +#[test] +fn try_from() { + // Empty is allowed. + assert_eq!( + AllowedAscii::try_from_unique_ascii([]), + Ok(AllowedAscii { allowed: [] }) + ); + // Duplicates are not allowed. + assert_eq!( + AllowedAscii::try_from_unique_ascii(b"aba".to_owned()), + Err(AsciiErr::Duplicate(b'a')) + ); + // `b'.'` is not allowed. + assert_eq!( + AllowedAscii::try_from_unique_ascii(b"a.c".to_owned()), + Err(AsciiErr::Contains46) + ); + // At most 127 bytes are allowed. + assert_eq!( + AllowedAscii::try_from_unique_ascii([0; 128]), + Err(AsciiErr::CountTooLarge(128)) + ); + let mut all_ascii = (0..b'.').collect::<Vec<u8>>(); + let next = b'.' + 1; + all_ascii.extend(next..=127); + assert_eq!( + AllowedAscii::try_from_unique_ascii(all_ascii.clone()), + Ok(AllowedAscii { allowed: all_ascii }) + ); + // Only ASCII is allowed. + assert_eq!( + AllowedAscii::try_from_unique_ascii([255]), + Err(AsciiErr::InvalidByte(255)) + ); + assert!( + AllowedAscii::try_from_unique_ascii(b"abcdef".to_owned()).is_ok_and(|bytes| bytes + .as_inner() + .contains(&b'a') + && bytes.contains(b'b') + && bytes.contains(b'c') + && bytes.contains(b'd') + && bytes.contains(b'e') + && bytes.contains(b'f')) + ); +} +#[expect( + clippy::cognitive_complexity, + clippy::too_many_lines, + reason = "want to test a lot of things" +)] +#[test] +fn consts() { + let letters = ASCII_LETTERS; + assert_eq!(letters.len(), 52); + for i in b'A'..=b'Z' { + assert!(letters.contains(i)); + } + for i in b'a'..=b'z' { + assert!(letters.contains(i)); + } + let digits = ASCII_DIGITS; + assert_eq!(digits.len(), 10); + for i in b'0'..=b'9' { + assert!(digits.contains(i)); + } + let lower = ASCII_LOWERCASE; + assert_eq!(lower.len(), 26); + for i in b'a'..=b'z' { + assert!(lower.contains(i)); + } + let upper = ASCII_UPPERCASE; + assert_eq!(upper.len(), 26); + for i in b'A'..=b'Z' { + assert!(upper.contains(i)); + } + let dig_let = ASCII_DIGITS_LETTERS; + assert_eq!(dig_let.len(), 62); + for i in b'a'..=b'z' { + assert!(dig_let.contains(i)); + } + for i in b'0'..=b'9' { + assert!(dig_let.contains(i)); + } + for i in b'A'..=b'Z' { + assert!(dig_let.contains(i)); + } + let dig_lower = ASCII_DIGITS_LOWERCASE; + assert_eq!(dig_lower.len(), 36); + for i in b'a'..=b'z' { + assert!(dig_lower.contains(i)); + } + for i in b'0'..=b'9' { + assert!(dig_lower.contains(i)); + } + let dig_upper = ASCII_DIGITS_UPPERCASE; + assert_eq!(dig_upper.len(), 36); + for i in b'A'..=b'Z' { + assert!(dig_upper.contains(i)); + } + for i in b'0'..=b'9' { + assert!(dig_upper.contains(i)); + } + let ffox = ASCII_FIREFOX; + assert_eq!(ffox.len(), 78); + for i in b'A'..=b'Z' { + assert!(ffox.contains(i)); + } + for i in b'a'..=b'z' { + assert!(ffox.contains(i)); + } + for i in b'0'..=b'9' { + assert!(ffox.contains(i)); + } + assert!(ffox.contains(b'!')); + assert!(ffox.contains(b'$')); + assert!(ffox.contains(b'&')); + assert!(ffox.contains(b'\'')); + assert!(ffox.contains(b'(')); + assert!(ffox.contains(b')')); + assert!(ffox.contains(b'+')); + assert!(ffox.contains(b',')); + assert!(ffox.contains(b'-')); + assert!(ffox.contains(b';')); + assert!(ffox.contains(b'=')); + assert!(ffox.contains(b'_')); + assert!(ffox.contains(b'`')); + assert!(ffox.contains(b'{')); + assert!(ffox.contains(b'}')); + assert!(ffox.contains(b'~')); + assert_eq!(ASCII_HYPHEN_DIGITS_LETTERS.len(), 63); + assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(b'-')); + for i in b'A'..=b'Z' { + assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); + } + for i in b'a'..=b'z' { + assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); + } + for i in b'0'..=b'9' { + assert!(ASCII_HYPHEN_DIGITS_LETTERS.contains(i)); + } + let hyp_lower = ASCII_HYPHEN_DIGITS_LOWERCASE; + assert_eq!(hyp_lower.len(), 37); + assert!(hyp_lower.contains(b'-')); + for i in b'a'..=b'z' { + assert!(hyp_lower.contains(i)); + } + for i in b'0'..=b'9' { + assert!(hyp_lower.contains(i)); + } + let hyp_upper = ASCII_HYPHEN_DIGITS_UPPERCASE; + assert_eq!(hyp_upper.len(), 37); + assert!(hyp_upper.contains(b'-')); + for i in b'A'..=b'Z' { + assert!(hyp_upper.contains(i)); + } + for i in b'0'..=b'9' { + assert!(hyp_upper.contains(i)); + } + let printable = PRINTABLE_ASCII; + assert_eq!(printable.len(), 92); + let stop = b'.' - 1; + for i in 33..=stop { + assert!(printable.contains(i)); + } + let stop2 = b'\\' - 1; + for i in stop + 2..=stop2 { + assert!(printable.contains(i)); + } + for i in stop2 + 2..=b'~' { + assert!(printable.contains(i)); + } + let rfc = RFC5322_ATEXT; + assert_eq!(rfc.len(), 81); + for i in b'A'..=b'Z' { + assert!(rfc.contains(i)); + } + for i in b'a'..=b'z' { + assert!(rfc.contains(i)); + } + for i in b'0'..=b'9' { + assert!(rfc.contains(i)); + } + assert!(rfc.contains(b'!')); + assert!(rfc.contains(b'#')); + assert!(rfc.contains(b'$')); + assert!(rfc.contains(b'%')); + assert!(rfc.contains(b'&')); + assert!(rfc.contains(b'\'')); + assert!(rfc.contains(b'*')); + assert!(rfc.contains(b'+')); + assert!(rfc.contains(b'-')); + assert!(rfc.contains(b'/')); + assert!(rfc.contains(b'=')); + assert!(rfc.contains(b'?')); + assert!(rfc.contains(b'^')); + assert!(rfc.contains(b'_')); + assert!(rfc.contains(b'`')); + assert!(rfc.contains(b'{')); + assert!(rfc.contains(b'|')); + assert!(rfc.contains(b'}')); + assert!(rfc.contains(b'~')); + let whatwg = WHATWG_VALID_DOMAIN_CODE_POINTS; + for i in 0..=0x1f { + assert!(!whatwg.contains(i)); + } + assert!(!whatwg.contains(b'\x20')); + assert!(!whatwg.contains(b'#')); + assert!(!whatwg.contains(b'/')); + assert!(!whatwg.contains(b':')); + assert!(!whatwg.contains(b'<')); + assert!(!whatwg.contains(b'>')); + assert!(!whatwg.contains(b'?')); + assert!(!whatwg.contains(b'@')); + assert!(!whatwg.contains(b'[')); + assert!(!whatwg.contains(b'\\')); + assert!(!whatwg.contains(b']')); + assert!(!whatwg.contains(b'^')); + assert!(!whatwg.contains(b'|')); + assert!(!whatwg.contains(b'%')); + assert!(!whatwg.contains(b'\x7f')); + assert!(!whatwg.contains(b'.')); + assert_eq!(whatwg.len(), 128 - 32 - 16); +} diff --git a/src/dom.rs b/src/dom.rs @@ -1,4 +1,7 @@ extern crate alloc; +#[cfg(test)] +/// Unit tests. +mod tests; use crate::char_set::{ASCII_HYPHEN_DIGITS_LETTERS, AllowedAscii}; use alloc::{string::String, vec::Vec}; use core::{ @@ -1505,341 +1508,3 @@ impl<'a> IntoIterator for Rfc1123Domain<&'a [u8]> { } } } -#[cfg(test)] -mod tests { - extern crate alloc; - use super::{Domain, DomainErr, Rfc1123Domain, Rfc1123Err}; - use crate::char_set::{ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS, AllowedAscii}; - use alloc::borrow::ToOwned as _; - use core::cmp::Ordering; - use serde_json as _; - #[test] - fn dom_parse() { - let allowed_ascii = ASCII_FIREFOX; - // Test empty is error. - assert!( - Domain::try_from_bytes("", &allowed_ascii) - .map_or_else(|e| e == DomainErr::Empty, |_| false) - ); - // Test root domain. - assert!( - Domain::try_from_bytes(".", &allowed_ascii) - .map_or_else(|e| e == DomainErr::RootDomain, |_| false) - ); - // Test empty label is error. - assert!( - Domain::try_from_bytes("a..com", &allowed_ascii) - .map_or_else(|e| e == DomainErr::EmptyLabel, |_| false) - ); - assert!( - Domain::try_from_bytes("a..", &allowed_ascii) - .map_or_else(|e| e == DomainErr::EmptyLabel, |_| false) - ); - assert!( - Domain::try_from_bytes("..", &allowed_ascii) - .map_or_else(|e| e == DomainErr::EmptyLabel, |_| false) - ); - // Test label too long. - let val = "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"; - // 4 + 64 + 4 - assert!(val.len() == 72); - assert!( - Domain::try_from_bytes(val, &allowed_ascii) - .map_or_else(|e| e == DomainErr::LabelLenExceeds63, |_| false) - ); - assert!( - Domain::try_from_bytes( - "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", - &allowed_ascii - ) - .is_ok_and(|d| d.len().get() == 71) - ); - // Test domain too long. - assert!(Domain::try_from_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &allowed_ascii).is_err_and(|e| e == DomainErr::LenExceeds253(254))); - assert!(Domain::try_from_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &allowed_ascii).is_ok_and(|d| d.len().get() == 253 )); - // Test max labels. - assert!(Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", &allowed_ascii).is_err_and(|e| e == DomainErr::LenExceeds253(255))); - assert!(Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", &allowed_ascii).is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); - assert!(Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.", &allowed_ascii).is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); - // Test removal of trailing '.'. - assert!(Domain::try_from_bytes("com.", &allowed_ascii).is_ok_and(|d| d.as_str() == "com")); - // Test single label. - assert!(Domain::try_from_bytes("c", &allowed_ascii).is_ok_and(|d| d.as_str() == "c")); - // Test case-insensitivity. - assert!( - Domain::try_from_bytes("wwW.ExAMple.COm", &allowed_ascii).is_ok_and(|d| { - Domain::try_from_bytes("www.example.com", &allowed_ascii) - .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) - }) - ); - assert!( - Domain::try_from_bytes("ww_W.com", &allowed_ascii).is_ok_and(|d| { - Domain::try_from_bytes("Ww_w.com", &allowed_ascii) - .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) - }) - ); - // Test valid bytes - let mut input; - let mut counter = 0u8; - for i in 0..=127 { - input = [i]; - match i { - b'!' - | b'$' - | b'&'..=b')' - | b'+'..=b'-' - | b'0'..=b'9' - | b';' - | b'=' - | b'A'..=b'Z' - | b'_'..=b'{' - | b'}'..=b'~' => { - counter += 1; - assert!( - Domain::try_from_bytes(input, &allowed_ascii) - .is_ok_and(|d| d.value.len() == 1 && d.value == input) - ); - } - b'.' => { - let input2 = b"a."; - assert!( - Domain::try_from_bytes(input2, &allowed_ascii) - .is_ok_and(|d| d.len().get() == 1 && d.value == input2) - ); - } - _ => assert!( - Domain::try_from_bytes(input, &allowed_ascii) - .is_err_and(|e| e == DomainErr::InvalidByte(i)) - ), - } - } - assert!(counter == 78); - } - #[test] - fn dom_iter() { - let allowed_ascii = ASCII_FIREFOX; - assert!( - Domain::try_from_bytes("www.example.com", &allowed_ascii).is_ok_and(|d| { - let mut iter = d.iter(); - let Some(l) = iter.next() else { - return false; - }; - if l.value != "com" { - return false; - } - let Some(l2) = iter.next() else { return false }; - if l2.value != "example" { - return false; - } - let Some(l3) = iter.next() else { - return false; - }; - if l3.value != "www" { - return false; - } - iter.next().is_none() - }) - ); - assert!( - Domain::try_from_bytes("www.example.com", &allowed_ascii).is_ok_and(|d| { - let mut iter = d.iter(); - let Some(l) = iter.next_back() else { - return false; - }; - if l.value != "www" { - return false; - } - let Some(l2) = iter.next_back() else { - return false; - }; - if l2.value != "example" { - return false; - } - let Some(l3) = iter.next_back() else { - return false; - }; - if l3.value != "com" { - return false; - } - iter.next_back().is_none() - }) - ); - assert!( - Domain::try_from_bytes("www.example.com", &allowed_ascii).is_ok_and(|d| { - let mut iter = d.iter(); - let Some(l) = iter.next_back() else { - return false; - }; - if l.value != "www" { - return false; - } - let Some(l2) = iter.next() else { return false }; - if l2.value != "com" { - return false; - } - let Some(l3) = iter.next_back() else { - return false; - }; - if l3.value != "example" { - return false; - } - iter.next().is_none() && iter.next_back().is_none() - }) - ); - } - #[test] - fn rfc1123() { - assert!( - Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_ok_and(|rfc_dom| rfc_dom.as_str() == "example.com")) - ); - assert!( - AllowedAscii::try_from_unique_ascii(b"exampl!co".to_owned()).is_ok_and(|ascii| { - Domain::try_from_bytes("exampl!e.com", &ascii).is_ok_and(|dom| { - Rfc1123Domain::try_from(dom) - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::InvalidByte(b'!'))) - }) - }) - ); - assert!( - Domain::try_from_bytes("example-.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_err_and(|e| e == Rfc1123Err::LabelEndsWithAHyphen)) - ); - assert!( - Domain::try_from_bytes("-example.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_err_and(|e| e == Rfc1123Err::LabelStartsWithAHyphen)) - ); - assert!( - Domain::try_from_bytes("example.c1m", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { - Rfc1123Domain::try_from(dom).is_err_and(|e| e == Rfc1123Err::InvalidTld) - }) - ); - assert!( - Domain::try_from_bytes("example.commm", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_ok_and(|rfc| rfc.is_literal_interpretation())) - ); - assert!( - Domain::try_from_bytes("example.xn--abc", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_ok_and(|rfc| !rfc.is_literal_interpretation())) - ); - assert!( - Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_ok_and(|rfc| rfc.is_strict_interpretation())) - ); - assert!( - Domain::try_from_bytes("example.comm", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| Rfc1123Domain::try_from(dom) - .is_ok_and(|rfc| !rfc.is_strict_interpretation())) - ); - } - #[test] - fn tld() { - assert!( - Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| dom.tld().as_str() == "com",) - ); - } - #[expect(clippy::cognitive_complexity, reason = "want to test many things")] - #[test] - fn rfc1123_parse() { - // Test empty is error. - assert!( - Rfc1123Domain::try_from_bytes("") - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::Empty)) - ); - // Test root domain. - assert!( - Rfc1123Domain::try_from_bytes(".") - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::RootDomain)) - ); - // Test empty label is error. - assert!( - Rfc1123Domain::try_from_bytes("a..com") - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::EmptyLabel)) - ); - assert!( - Rfc1123Domain::try_from_bytes("a..") - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::EmptyLabel)) - ); - assert!( - Rfc1123Domain::try_from_bytes("..") - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::EmptyLabel)) - ); - // Test label too long. - let val = "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"; - // 4 + 64 + 4 - assert!(val.len() == 72); - assert!( - Rfc1123Domain::try_from_bytes(val) - .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::LabelLenExceeds63),) - ); - assert!( - Rfc1123Domain::try_from_bytes( - "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", - ) - .is_ok_and(|d| d.len().get() == 71) - ); - // Test domain too long. - assert!(Rfc1123Domain::try_from_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::LenExceeds253(254)))); - assert!(Rfc1123Domain::try_from_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").is_ok_and(|d| d.len().get() == 253 )); - // Test max labels. - assert!(Rfc1123Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::LenExceeds253(255)))); - assert!(Rfc1123Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); - assert!(Rfc1123Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.").is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); - // Test removal of trailing '.'. - assert!(Rfc1123Domain::try_from_bytes("com.").is_ok_and(|d| d.as_str() == "com")); - // Test single label. - assert!(Rfc1123Domain::try_from_bytes("c").is_ok_and(|d| d.as_str() == "c")); - // Test ends with hyphen. - assert!( - Rfc1123Domain::try_from_bytes("-") - .map_or_else(|err| err == Rfc1123Err::LabelEndsWithAHyphen, |_| false) - ); - assert!( - Rfc1123Domain::try_from_bytes("-.") - .map_or_else(|err| err == Rfc1123Err::LabelEndsWithAHyphen, |_| false) - ); - assert!( - Rfc1123Domain::try_from_bytes("a.com.-") - .map_or_else(|err| err == Rfc1123Err::LabelEndsWithAHyphen, |_| false) - ); - assert!( - Rfc1123Domain::try_from_bytes("a.com-") - .map_or_else(|err| err == Rfc1123Err::LabelEndsWithAHyphen, |_| false) - ); - assert!( - Rfc1123Domain::try_from_bytes("a-.com") - .map_or_else(|err| err == Rfc1123Err::LabelEndsWithAHyphen, |_| false) - ); - // Test starts with hyphen. - assert!( - Rfc1123Domain::try_from_bytes("a.-com") - .map_or_else(|err| err == Rfc1123Err::LabelStartsWithAHyphen, |_| false) - ); - assert!( - Rfc1123Domain::try_from_bytes("-a.com") - .map_or_else(|err| err == Rfc1123Err::LabelStartsWithAHyphen, |_| false) - ); - // Test case-insensitivity. - assert!( - Rfc1123Domain::try_from_bytes("wwW.ExAMple.COm").is_ok_and(|d| { - Rfc1123Domain::try_from_bytes("www.example.com") - .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) - }) - ); - assert!(Rfc1123Domain::try_from_bytes("ww-W.com").is_ok_and(|d| { - Rfc1123Domain::try_from_bytes("Ww-w.com") - .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) - })); - assert!( - Rfc1123Domain::try_from_bytes("1.1.1.1") - .map_or_else(|err| err == Rfc1123Err::InvalidTld, |_| false) - ); - } -} diff --git a/src/dom/tests.rs b/src/dom/tests.rs @@ -0,0 +1,357 @@ +extern crate alloc; +use super::{Domain, DomainErr, Rfc1123Domain, Rfc1123Err}; +use crate::char_set::{ASCII_FIREFOX, ASCII_HYPHEN_DIGITS_LETTERS, AllowedAscii}; +use alloc::borrow::ToOwned as _; +use core::cmp::Ordering; +use serde_json as _; +#[test] +fn dom_parse() { + let allowed_ascii = ASCII_FIREFOX; + // Test empty is error. + assert_eq!( + Domain::try_from_bytes("", &allowed_ascii), + Err(DomainErr::Empty) + ); + // Test root domain. + assert_eq!( + Domain::try_from_bytes(".", &allowed_ascii), + Err(DomainErr::RootDomain) + ); + // Test empty label is error. + assert_eq!( + Domain::try_from_bytes("a..com", &allowed_ascii), + Err(DomainErr::EmptyLabel) + ); + assert_eq!( + Domain::try_from_bytes("a..", &allowed_ascii), + Err(DomainErr::EmptyLabel) + ); + assert_eq!( + Domain::try_from_bytes("..", &allowed_ascii), + Err(DomainErr::EmptyLabel) + ); + // Test label too long. + let val = "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"; + // 4 + 64 + 4 + assert_eq!(val.len(), 72); + assert_eq!( + Domain::try_from_bytes(val, &allowed_ascii), + Err(DomainErr::LabelLenExceeds63) + ); + assert!( + Domain::try_from_bytes( + "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", + &allowed_ascii + ) + .is_ok_and(|d| d.len().get() == 71) + ); + // Test domain too long. + assert_eq!( + Domain::try_from_bytes( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + &allowed_ascii + ), + Err(DomainErr::LenExceeds253(254)) + ); + assert!(Domain::try_from_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", &allowed_ascii).is_ok_and(|d| d.len().get() == 253 )); + // Test max labels. + assert_eq!( + Domain::try_from_bytes( + "a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", + &allowed_ascii + ), + Err(DomainErr::LenExceeds253(255)) + ); + assert!(Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a", &allowed_ascii).is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); + assert!(Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.", &allowed_ascii).is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); + // Test removal of trailing '.'. + assert!(Domain::try_from_bytes("com.", &allowed_ascii).is_ok_and(|d| d.as_str() == "com")); + // Test single label. + assert!(Domain::try_from_bytes("c", &allowed_ascii).is_ok_and(|d| d.as_str() == "c")); + // Test case-insensitivity. + assert!( + Domain::try_from_bytes("wwW.ExAMple.COm", &allowed_ascii).is_ok_and(|d| { + Domain::try_from_bytes("www.example.com", &allowed_ascii) + .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) + }) + ); + assert!( + Domain::try_from_bytes("ww_W.com", &allowed_ascii).is_ok_and(|d| { + Domain::try_from_bytes("Ww_w.com", &allowed_ascii) + .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) + }) + ); + // Test valid bytes + let mut input; + let mut counter = 0u8; + for i in 0..=127 { + input = [i]; + match i { + b'!' + | b'$' + | b'&'..=b')' + | b'+'..=b'-' + | b'0'..=b'9' + | b';' + | b'=' + | b'A'..=b'Z' + | b'_'..=b'{' + | b'}'..=b'~' => { + counter += 1; + assert!( + Domain::try_from_bytes(input, &allowed_ascii) + .is_ok_and(|d| d.value.len() == 1 && d.value == input) + ); + } + b'.' => { + let input2 = b"a."; + assert!( + Domain::try_from_bytes(input2, &allowed_ascii) + .is_ok_and(|d| d.len().get() == 1 && d.value == input2) + ); + } + _ => assert_eq!( + Domain::try_from_bytes(input, &allowed_ascii), + Err(DomainErr::InvalidByte(i)) + ), + } + } + assert_eq!(counter, 78); +} +#[test] +fn dom_iter() { + let allowed_ascii = ASCII_FIREFOX; + assert!( + Domain::try_from_bytes("www.example.com", &allowed_ascii).is_ok_and(|d| { + let mut iter = d.iter(); + let Some(l) = iter.next() else { + return false; + }; + if l.value != "com" { + return false; + } + let Some(l2) = iter.next() else { return false }; + if l2.value != "example" { + return false; + } + let Some(l3) = iter.next() else { + return false; + }; + if l3.value != "www" { + return false; + } + iter.next().is_none() + }) + ); + assert!( + Domain::try_from_bytes("www.example.com", &allowed_ascii).is_ok_and(|d| { + let mut iter = d.iter(); + let Some(l) = iter.next_back() else { + return false; + }; + if l.value != "www" { + return false; + } + let Some(l2) = iter.next_back() else { + return false; + }; + if l2.value != "example" { + return false; + } + let Some(l3) = iter.next_back() else { + return false; + }; + if l3.value != "com" { + return false; + } + iter.next_back().is_none() + }) + ); + assert!( + Domain::try_from_bytes("www.example.com", &allowed_ascii).is_ok_and(|d| { + let mut iter = d.iter(); + let Some(l) = iter.next_back() else { + return false; + }; + if l.value != "www" { + return false; + } + let Some(l2) = iter.next() else { return false }; + if l2.value != "com" { + return false; + } + let Some(l3) = iter.next_back() else { + return false; + }; + if l3.value != "example" { + return false; + } + iter.next().is_none() && iter.next_back().is_none() + }) + ); +} +#[test] +fn rfc1123() { + assert!( + Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS) + .is_ok_and(|dom| Rfc1123Domain::try_from(dom) + .is_ok_and(|rfc_dom| rfc_dom.as_str() == "example.com")) + ); + assert!( + AllowedAscii::try_from_unique_ascii(b"exampl!co".to_owned()).is_ok_and(|ascii| { + Domain::try_from_bytes("exampl!e.com", &ascii).is_ok_and(|dom| { + Rfc1123Domain::try_from(dom) + .is_err_and(|e| e == Rfc1123Err::DomainErr(DomainErr::InvalidByte(b'!'))) + }) + }) + ); + assert!( + Domain::try_from_bytes("example-.com", &ASCII_HYPHEN_DIGITS_LETTERS) + .is_ok_and(|dom| Rfc1123Domain::try_from(dom) + .is_err_and(|e| e == Rfc1123Err::LabelEndsWithAHyphen)) + ); + assert!( + Domain::try_from_bytes("-example.com", &ASCII_HYPHEN_DIGITS_LETTERS) + .is_ok_and(|dom| Rfc1123Domain::try_from(dom) + .is_err_and(|e| e == Rfc1123Err::LabelStartsWithAHyphen)) + ); + assert!( + Domain::try_from_bytes("example.c1m", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { + Rfc1123Domain::try_from(dom).is_err_and(|e| e == Rfc1123Err::InvalidTld) + }) + ); + assert!( + Domain::try_from_bytes("example.commm", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { + Rfc1123Domain::try_from(dom).is_ok_and(|rfc| rfc.is_literal_interpretation()) + }) + ); + assert!( + Domain::try_from_bytes("example.xn--abc", &ASCII_HYPHEN_DIGITS_LETTERS) + .is_ok_and(|dom| Rfc1123Domain::try_from(dom) + .is_ok_and(|rfc| !rfc.is_literal_interpretation())) + ); + assert!( + Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { + Rfc1123Domain::try_from(dom).is_ok_and(|rfc| rfc.is_strict_interpretation()) + }) + ); + assert!( + Domain::try_from_bytes("example.comm", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { + Rfc1123Domain::try_from(dom).is_ok_and(|rfc| !rfc.is_strict_interpretation()) + }) + ); +} +#[test] +fn tld() { + assert!( + Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS) + .is_ok_and(|dom| dom.tld().as_str() == "com",) + ); +} +#[expect(clippy::cognitive_complexity, reason = "want to test many things")] +#[test] +fn rfc1123_parse() { + // Test empty is error. + assert_eq!( + Rfc1123Domain::try_from_bytes(""), + Err(Rfc1123Err::DomainErr(DomainErr::Empty)) + ); + // Test root domain. + assert_eq!( + Rfc1123Domain::try_from_bytes("."), + Err(Rfc1123Err::DomainErr(DomainErr::RootDomain)) + ); + // Test empty label is error. + assert_eq!( + Rfc1123Domain::try_from_bytes("a..com"), + Err(Rfc1123Err::DomainErr(DomainErr::EmptyLabel)) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes("a.."), + Err(Rfc1123Err::DomainErr(DomainErr::EmptyLabel)) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes(".."), + Err(Rfc1123Err::DomainErr(DomainErr::EmptyLabel)) + ); + // Test label too long. + let val = "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com"; + // 4 + 64 + 4 + assert_eq!(val.len(), 72); + assert_eq!( + Rfc1123Domain::try_from_bytes(val), + Err(Rfc1123Err::DomainErr(DomainErr::LabelLenExceeds63),) + ); + assert!( + Rfc1123Domain::try_from_bytes( + "www.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.com", + ) + .is_ok_and(|d| d.len().get() == 71) + ); + // Test domain too long. + assert_eq!( + Rfc1123Domain::try_from_bytes( + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + ), + Err(Rfc1123Err::DomainErr(DomainErr::LenExceeds253(254))) + ); + assert!(Rfc1123Domain::try_from_bytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").is_ok_and(|d| d.len().get() == 253 )); + // Test max labels. + assert_eq!( + Rfc1123Domain::try_from_bytes( + "a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a" + ), + Err(Rfc1123Err::DomainErr(DomainErr::LenExceeds253(255))) + ); + assert!(Rfc1123Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a").is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); + assert!(Rfc1123Domain::try_from_bytes("a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.a.").is_ok_and(|d| d.iter().count() == 127 && d.len().get() == 253)); + // Test removal of trailing '.'. + assert!(Rfc1123Domain::try_from_bytes("com.").is_ok_and(|d| d.as_str() == "com")); + // Test single label. + assert!(Rfc1123Domain::try_from_bytes("c").is_ok_and(|d| *d.as_inner() == "c")); + // Test ends with hyphen. + assert_eq!( + Rfc1123Domain::try_from_bytes("-"), + Err(Rfc1123Err::LabelEndsWithAHyphen) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes("-."), + Err(Rfc1123Err::LabelEndsWithAHyphen) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes("a.com.-"), + Err(Rfc1123Err::LabelEndsWithAHyphen) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes("a.com-"), + Err(Rfc1123Err::LabelEndsWithAHyphen) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes("a-.com"), + Err(Rfc1123Err::LabelEndsWithAHyphen) + ); + // Test starts with hyphen. + assert_eq!( + Rfc1123Domain::try_from_bytes("a.-com"), + Err(Rfc1123Err::LabelStartsWithAHyphen) + ); + assert_eq!( + Rfc1123Domain::try_from_bytes("-a.com"), + Err(Rfc1123Err::LabelStartsWithAHyphen) + ); + // Test case-insensitivity. + assert!( + Rfc1123Domain::try_from_bytes("wwW.ExAMple.COm").is_ok_and(|d| { + Rfc1123Domain::try_from_bytes("www.example.com") + .is_ok_and(|d2| d == d2 && d.cmp(&d2) == Ordering::Equal) + }) + ); + assert!(Rfc1123Domain::try_from_bytes("ww-W.com").is_ok_and(|d| { + Rfc1123Domain::try_from_bytes("Ww-w.com") + .is_ok_and(|d2| d.domain() == d2.domain() && d.cmp(&d2) == Ordering::Equal) + })); + assert_eq!( + Rfc1123Domain::try_from_bytes("1.1.1.1"), + Err(Rfc1123Err::InvalidTld) + ); +} diff --git a/src/serde.rs b/src/serde.rs @@ -1,4 +1,7 @@ extern crate alloc; +#[cfg(test)] +/// Unit tests. +mod tests; use crate::{ char_set::{AllowedAscii, PRINTABLE_ASCII}, dom::{Domain, DomainErr, Rfc1123Domain, Rfc1123Err}, @@ -233,51 +236,3 @@ impl<'de: 'a, 'a> Deserialize<'de> for Rfc1123Domain<&'a str> { deserializer.deserialize_str(Rfc1123Visitor::<&'a str>(PhantomData)) } } -#[cfg(test)] -mod tests { - extern crate alloc; - use crate::{ - char_set::ASCII_HYPHEN_DIGITS_LETTERS, - dom::{Domain, Rfc1123Domain}, - }; - use alloc::string::String; - #[test] - fn serde() { - assert!( - serde_json::from_str::<Domain<&str>>(r#""example.com""#) - .is_ok_and(|dom| dom.into_iter().count() == 2) - ); - assert!( - serde_json::from_str::<Domain<String>>(r#""c\"om""#) - .is_ok_and(|dom| dom.into_iter().count() == 1) - ); - // Can't borrow since input needs to be de-escaped. - assert!( - serde_json::from_str::<Domain<&str>>(r#""c\"om""#) - .is_err_and(|err| err.is_data() && err.column() == 7) - ); - assert!( - Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| serde_json::to_string(&dom) - .is_ok_and(|output| output == r#""example.com""#)) - ); - assert!( - Domain::try_from_bytes(b"example.com", &ASCII_HYPHEN_DIGITS_LETTERS) - .is_ok_and(|dom| serde_json::to_string(&dom) - .is_ok_and(|output| output == r#""example.com""#)) - ); - assert!( - serde_json::from_str::<Rfc1123Domain<&str>>(r#""example.com""#) - .is_ok_and(|dom| dom.into_iter().count() == 2) - ); - assert!( - serde_json::from_str::<Rfc1123Domain<String>>(r#""c\u006fm""#) - .is_ok_and(|dom| dom.tld().as_str() == "com") - ); - // Can't borrow since input needs to be de-escaped. - assert!( - serde_json::from_str::<Rfc1123Domain<&str>>(r#""c\u006fm""#) - .is_err_and(|err| err.is_data() && err.column() == 10) - ); - } -} diff --git a/src/serde/tests.rs b/src/serde/tests.rs @@ -0,0 +1,45 @@ +extern crate alloc; +use crate::{ + char_set::ASCII_HYPHEN_DIGITS_LETTERS, + dom::{Domain, Rfc1123Domain}, +}; +use alloc::string::String; +#[test] +fn serde() { + assert!( + serde_json::from_str::<Domain<&str>>(r#""example.com""#) + .is_ok_and(|dom| dom.into_iter().count() == 2) + ); + assert!( + serde_json::from_str::<Domain<String>>(r#""c\"om""#) + .is_ok_and(|dom| dom.into_iter().count() == 1) + ); + // Can't borrow since input needs to be de-escaped. + assert!( + serde_json::from_str::<Domain<&str>>(r#""c\"om""#) + .is_err_and(|err| err.is_data() && err.column() == 7) + ); + assert!( + Domain::try_from_bytes("example.com", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { + serde_json::to_string(&dom).is_ok_and(|output| output == r#""example.com""#) + }) + ); + assert!( + Domain::try_from_bytes(b"example.com", &ASCII_HYPHEN_DIGITS_LETTERS).is_ok_and(|dom| { + serde_json::to_string(&dom).is_ok_and(|output| output == r#""example.com""#) + }) + ); + assert!( + serde_json::from_str::<Rfc1123Domain<&str>>(r#""example.com""#) + .is_ok_and(|dom| dom.into_iter().count() == 2) + ); + assert!( + serde_json::from_str::<Rfc1123Domain<String>>(r#""c\u006fm""#) + .is_ok_and(|dom| dom.tld().as_str() == "com") + ); + // Can't borrow since input needs to be de-escaped. + assert!( + serde_json::from_str::<Rfc1123Domain<&str>>(r#""c\u006fm""#) + .is_err_and(|err| err.is_data() && err.column() == 10) + ); +}