pub fn valid_name_code_point(cp: u32) -> bool {
matches!(
cp,
0x002D | 0x0030..=0x0039 | 0x0061..=0x007A
| 0x00B7
| 0x00C0..=0x00D6 | 0x00D8..=0x00F6 | 0x00F8..=0x037D
| 0x037F..=0x1FFF
| 0x200C | 0x200D
| 0x203F | 0x2040
| 0x2070..=0x218F
| 0x2C00..=0x2FEF
| 0x3001..=0xD7FF
| 0xF900..=0xFDCF
| 0xFDF0..=0xFFFD
| 0x10000..=0xEFFFF
)
}
pub fn valid_name_code_point_first_position(cp: u32) -> bool {
match cp {
0x0030..=0x0039 => false, 0x002D => false, _ => is_letter(cp),
}
}
pub fn valid_name_code_point_other_position(cp: u32) -> bool {
match cp {
0x0030..=0x0039 => true, 0x002D => true, _ => is_letter(cp),
}
}
fn is_letter(cp: u32) -> bool {
matches!(
cp,
0x0041..=0x005A | 0x0061..=0x007A | 0x00C0..=0x00D6 | 0x00D8..=0x00F6 | 0x00F8..=0x00FF | 0x0100..=0x017F | 0x0180..=0x024F | 0x0370..=0x03FF | 0x0400..=0x04FF | 0x0590..=0x05FF | 0x0600..=0x06FF | 0x4E00..=0x9FFF | 0x3040..=0x309F | 0x30A0..=0x30FF | 0xAC00..=0xD7AF | 0x0E00..=0x0E7F )
}
pub fn contains_forbidden_domain_code_point(input: &str) -> bool {
for c in input.chars() {
let cp = c as u32;
match cp {
0x0000..=0x001F | 0x007F..=0x009F => return true,
0x0020 | 0x0022 | 0x0023 | 0x0025 | 0x002F => return true,
0x003A | 0x003C | 0x003E | 0x003F | 0x0040 => return true,
0x005B | 0x005C | 0x005D | 0x005E | 0x007C => return true,
_ => continue,
}
}
false
}
pub fn is_ascii(input: &str) -> bool {
input.is_ascii()
}
pub fn is_label_valid(label: &str) -> bool {
if label.is_empty() || label.len() > 63 {
return false;
}
if label.starts_with('-') || label.ends_with('-') {
return false;
}
if let Some(stripped) = label.strip_prefix("xn--") {
return crate::punycode::verify_punycode(stripped);
}
for c in label.chars() {
if !valid_name_code_point(c as u32) {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_name_code_point() {
assert!(valid_name_code_point(b'a' as u32));
assert!(valid_name_code_point(b'0' as u32));
assert!(valid_name_code_point(b'-' as u32));
assert!(!valid_name_code_point(b' ' as u32));
}
#[test]
fn test_is_ascii() {
assert!(is_ascii("hello"));
assert!(!is_ascii("café"));
}
#[test]
fn test_is_label_valid() {
assert!(is_label_valid("hello"));
assert!(is_label_valid("test-domain"));
assert!(!is_label_valid("-invalid"));
assert!(!is_label_valid("invalid-"));
assert!(!is_label_valid(""));
}
#[test]
fn test_contains_forbidden_domain_code_point() {
assert!(!contains_forbidden_domain_code_point("example.com"));
assert!(contains_forbidden_domain_code_point("exam ple.com"));
assert!(contains_forbidden_domain_code_point("example.com/path"));
}
}