pub(crate) fn public_suffix(name: &str) -> Option<&str> {
let mut candidate = name;
loop {
let suffix = ::psl::suffix(candidate.as_bytes())?.trim();
let len = suffix.as_bytes().len();
let start = candidate.len().checked_sub(len)?;
let tail = candidate.get(start..)?;
match suffix.typ() {
Some(::psl::Type::Private) => {
let (_, rest) = tail.split_once('.')?;
candidate = rest;
}
_ => return Some(tail),
}
}
}
pub(crate) fn registrable_domain(name: &str) -> Option<&str> {
let suffix = public_suffix(name)?;
let prefix = name.strip_suffix(suffix)?.strip_suffix('.')?;
if prefix.is_empty() {
return None;
}
let label = prefix.rsplit('.').next()?;
let start = prefix.len() - label.len();
name.get(start..)
}
pub(crate) fn registrable_parent(name: &str) -> Option<&str> {
registrable_domain(name).filter(|reg| reg.len() < name.len())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn public_suffix_handles_multi_label_icann_suffixes() {
assert_eq!(public_suffix("example.com"), Some("com"));
assert_eq!(public_suffix("example.co.uk"), Some("co.uk"));
assert_eq!(public_suffix("mail.example.com.au"), Some("com.au"));
assert_eq!(public_suffix("example.co.jp"), Some("co.jp"));
assert_eq!(public_suffix("example.com.br"), Some("com.br"));
}
#[test]
fn public_suffix_skips_private_section_rules() {
assert_eq!(public_suffix("foo.github.io"), Some("io"));
assert_eq!(public_suffix("github.io"), Some("io"));
assert_eq!(public_suffix("foo.blogspot.co.uk"), Some("co.uk"));
}
#[test]
fn public_suffix_falls_back_to_tld_for_unlisted_tlds() {
assert_eq!(
public_suffix("example.notarealtld-seer"),
Some("notarealtld-seer")
);
}
#[test]
fn public_suffix_matches_punycode_idn_tlds() {
assert_eq!(public_suffix("example.xn--p1ai"), Some("xn--p1ai"));
}
#[test]
fn registrable_domain_is_one_label_plus_suffix() {
assert_eq!(registrable_domain("example.com"), Some("example.com"));
assert_eq!(registrable_domain("mail.google.com"), Some("google.com"));
assert_eq!(
registrable_domain("a.b.example.co.uk"),
Some("example.co.uk")
);
assert_eq!(registrable_domain("foo.github.io"), Some("github.io"));
}
#[test]
fn registrable_domain_is_none_for_bare_suffixes() {
assert_eq!(registrable_domain("com"), None);
assert_eq!(registrable_domain("co.uk"), None);
assert_eq!(registrable_domain(""), None);
}
#[test]
fn registrable_parent_only_for_names_below_the_registrable_domain() {
assert_eq!(registrable_parent("mail.google.com"), Some("google.com"));
assert_eq!(
registrable_parent("www.example.co.uk"),
Some("example.co.uk")
);
assert_eq!(registrable_parent("foo.github.io"), Some("github.io"));
assert_eq!(registrable_parent("google.com"), None);
assert_eq!(registrable_parent("example.co.uk"), None);
assert_eq!(registrable_parent("co.uk"), None);
}
}