pub mod dafsa;
pub mod dat;
mod system;
pub use dafsa::{DafsaFileLoadError, DafsaFilePublicSuffixList, SYSTEM_PSL_DAFSA_PATH};
pub use dat::{DatFileLoadError, DatFilePublicSuffixList, SYSTEM_PSL_PATH};
pub use system::{SystemLoadError, SystemPublicSuffixList};
pub trait PublicSuffixList: Send + Sync {
fn public_suffix(&self, host: &str) -> Option<String>;
fn registrable_domain(&self, host: &str) -> Option<String> {
let suffix = self.public_suffix(host)?;
if host == suffix {
return None;
}
let prefix = host.strip_suffix(&suffix)?.strip_suffix('.')?;
let last_label = prefix.rsplit('.').next()?;
Some(format!("{last_label}.{suffix}"))
}
}
#[cfg(test)]
pub(crate) struct MockPublicSuffixList;
#[cfg(test)]
impl PublicSuffixList for MockPublicSuffixList {
fn public_suffix(&self, host: &str) -> Option<String> {
const KNOWN_SUFFIXES: &[&str] = &["com", "co.uk", "org", "net"];
for suffix in KNOWN_SUFFIXES {
if host == *suffix {
return Some((*suffix).to_string());
}
let needle = format!(".{suffix}");
if host.ends_with(&needle) {
return Some((*suffix).to_string());
}
}
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mock_public_suffix_lookup() {
let psl = MockPublicSuffixList;
assert_eq!(psl.public_suffix("example.com").as_deref(), Some("com"));
assert_eq!(psl.public_suffix("com").as_deref(), Some("com"));
assert_eq!(psl.public_suffix("bbc.co.uk").as_deref(), Some("co.uk"));
assert_eq!(psl.public_suffix("co.uk").as_deref(), Some("co.uk"));
assert_eq!(psl.public_suffix("localhost"), None);
}
#[test]
fn mock_registrable_domain() {
let psl = MockPublicSuffixList;
assert_eq!(
psl.registrable_domain("login.example.com").as_deref(),
Some("example.com")
);
assert_eq!(
psl.registrable_domain("example.com").as_deref(),
Some("example.com")
);
assert_eq!(psl.registrable_domain("com"), None);
assert_eq!(
psl.registrable_domain("bbc.co.uk").as_deref(),
Some("bbc.co.uk")
);
assert_eq!(psl.registrable_domain("co.uk"), None);
}
}