use std::path::{Path, PathBuf};
use publicsuffix::{List, Psl};
use super::PublicSuffixList;
#[derive(thiserror::Error, Debug)]
pub enum DatFileLoadError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("invalid PSL data: {0}")]
Parse(String),
}
pub const SYSTEM_PSL_PATH: &str = "/usr/share/publicsuffix/public_suffix_list.dat";
pub struct DatFilePublicSuffixList {
list: List,
source: PathBuf,
}
impl std::fmt::Debug for DatFilePublicSuffixList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DatFilePublicSuffixList")
.field("source", &self.source)
.finish()
}
}
impl DatFilePublicSuffixList {
pub fn from_path(path: impl AsRef<Path>) -> Result<Self, DatFileLoadError> {
let path = path.as_ref();
let data = std::fs::read_to_string(path)?;
let list: List = data
.parse()
.map_err(|e: publicsuffix::Error| DatFileLoadError::Parse(e.to_string()))?;
Ok(Self {
list,
source: path.to_path_buf(),
})
}
pub fn from_system_file() -> Result<Self, DatFileLoadError> {
Self::from_path(SYSTEM_PSL_PATH)
}
}
impl PublicSuffixList for DatFilePublicSuffixList {
fn registrable_domain(&self, host: &str) -> Option<String> {
let suffix = self.list.suffix(host.as_bytes())?;
if !suffix.is_known() {
return None;
}
let domain = self.list.domain(host.as_bytes())?;
std::str::from_utf8(domain.as_bytes())
.ok()
.map(String::from)
}
fn public_suffix(&self, host: &str) -> Option<String> {
let suffix = self.list.suffix(host.as_bytes())?;
if !suffix.is_known() {
return None;
}
std::str::from_utf8(suffix.as_bytes())
.ok()
.map(String::from)
}
}