use std::collections::HashMap;
use crate::proxy::{Proxy, ProxyRotator};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FollowRedirects {
None,
Safe,
All,
}
#[derive(Debug, Clone)]
pub struct FetcherConfig {
pub impersonate: Impersonate,
pub stealthy_headers: bool,
pub proxy: Option<Proxy>,
pub timeout_secs: u64,
pub headers: HashMap<String, String>,
pub retries: u32,
pub retry_delay_secs: u64,
pub follow_redirects: FollowRedirects,
pub max_redirects: usize,
pub verify: bool,
}
impl Default for FetcherConfig {
fn default() -> Self {
Self {
impersonate: Impersonate::default(),
stealthy_headers: true,
proxy: None,
timeout_secs: 30,
headers: HashMap::new(),
retries: 3,
retry_delay_secs: 1,
follow_redirects: FollowRedirects::Safe,
max_redirects: 30,
verify: true,
}
}
}
#[derive(Debug, Clone)]
pub enum Impersonate {
None,
Single(String),
Random(Vec<String>),
}
impl Default for Impersonate {
fn default() -> Self {
Self::Single("chrome".to_owned())
}
}
impl Impersonate {
pub fn select(&self) -> Option<&str> {
match self {
Self::None => None,
Self::Single(s) => Some(s.as_str()),
Self::Random(list) => {
if list.is_empty() {
None
} else {
use rand::Rng;
let idx = rand::thread_rng().gen_range(0..list.len());
Some(list[idx].as_str())
}
}
}
}
}
pub struct FetcherConfigBuilder {
config: FetcherConfig,
proxy_rotator: Option<ProxyRotator>,
}
impl FetcherConfigBuilder {
pub fn new() -> Self {
Self {
config: FetcherConfig::default(),
proxy_rotator: None,
}
}
pub fn impersonate(mut self, imp: Impersonate) -> Self {
self.config.impersonate = imp;
self
}
pub fn stealthy_headers(mut self, enabled: bool) -> Self {
self.config.stealthy_headers = enabled;
self
}
pub fn proxy(mut self, proxy: Proxy) -> Self {
self.config.proxy = Some(proxy);
self
}
pub fn timeout_secs(mut self, secs: u64) -> Self {
self.config.timeout_secs = secs;
self
}
pub fn header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.config.headers.insert(name.into(), value.into());
self
}
pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
self.config.headers = headers;
self
}
pub fn retries(mut self, retries: u32) -> Self {
self.config.retries = retries;
self
}
pub fn retry_delay_secs(mut self, secs: u64) -> Self {
self.config.retry_delay_secs = secs;
self
}
pub fn follow_redirects(mut self, policy: FollowRedirects) -> Self {
self.config.follow_redirects = policy;
self
}
pub fn max_redirects(mut self, max: usize) -> Self {
self.config.max_redirects = max;
self
}
pub fn verify(mut self, verify: bool) -> Self {
self.config.verify = verify;
self
}
pub fn proxy_rotator(mut self, rotator: ProxyRotator) -> Self {
self.proxy_rotator = Some(rotator);
self
}
pub fn build(self) -> crate::error::Result<(FetcherConfig, Option<ProxyRotator>)> {
if self.proxy_rotator.is_some() && self.config.proxy.is_some() {
return Err(crate::error::FetchError::InvalidProxy(
"cannot use proxy_rotator together with static proxy".into(),
));
}
Ok((self.config, self.proxy_rotator))
}
}
impl Default for FetcherConfigBuilder {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Default)]
pub struct ParserConfig {
pub adaptive: bool,
pub adaptive_domain: String,
}