mod adblock;
mod domains;
mod hosts;
pub mod loader;
pub mod manager;
pub mod remote;
use std::io::BufRead;
pub use adblock::AdBlockParser;
pub use domains::DomainListParser;
pub use hosts::HostsFileParser;
use crate::config::BlocklistFormat;
#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("I/O error")]
Io(#[from] std::io::Error),
#[error("invalid line {line}: {reason}")]
InvalidLine {
line: usize,
reason: String,
},
}
pub trait BlocklistParser: Send + Sync {
fn parse(&self, reader: &mut dyn BufRead) -> Result<Vec<String>, ParseError>;
}
#[must_use]
pub fn parser_for_format(format: BlocklistFormat) -> Box<dyn BlocklistParser> {
match format {
BlocklistFormat::Domains => Box::new(DomainListParser),
BlocklistFormat::Hosts => Box::new(HostsFileParser),
BlocklistFormat::Adblock => Box::new(AdBlockParser),
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::BufReader;
#[test]
fn should_return_domain_parser_when_format_is_domains() {
let parser = parser_for_format(BlocklistFormat::Domains);
let content = "example.com\n*.test.com";
let domains = parser
.parse(&mut BufReader::new(content.as_bytes()))
.unwrap();
assert_eq!(domains, vec!["example.com", "*.test.com"]);
}
#[test]
fn should_return_hosts_parser_when_format_is_hosts() {
let parser = parser_for_format(BlocklistFormat::Hosts);
let content = "0.0.0.0 ads.example.com";
let domains = parser
.parse(&mut BufReader::new(content.as_bytes()))
.unwrap();
assert_eq!(domains, vec!["ads.example.com"]);
}
#[test]
fn should_return_adblock_parser_when_format_is_adblock() {
let parser = parser_for_format(BlocklistFormat::Adblock);
let content = "||ads.example.com^";
let domains = parser
.parse(&mut BufReader::new(content.as_bytes()))
.unwrap();
assert_eq!(domains, vec!["ads.example.com"]);
}
}