mod cache;
mod source;
use std::fmt;
use std::sync::Arc;
use std::time::Duration;
pub(crate) use cache::{RobotsCache, Verdict};
pub(crate) use source::{BackendSource, RobotsSource};
use crate::backend::Backend;
use crate::crawler::Tag;
use crate::crawler::connector::Connector;
#[derive(Clone)]
pub struct Robots {
pub(crate) agent: Arc<str>,
pub(crate) source: Option<Arc<dyn RobotsSource>>,
pub(crate) max_age: Duration,
pub(crate) retries: u32,
pub(crate) retry_delay: Duration,
pub(crate) sitemaps: Option<Tag>,
}
impl Robots {
pub fn new(user_agent: impl Into<Arc<str>>) -> Self {
Self {
agent: user_agent.into(),
source: None,
max_age: Duration::from_hours(24),
retries: 3,
retry_delay: Duration::from_secs(30),
sitemaps: None,
}
}
#[must_use]
pub fn with_backend<B: Backend>(mut self, backend: B) -> Self {
let connector = Connector::new(Arc::new(backend));
self.source = Some(Arc::new(BackendSource::new(Arc::new(connector))));
self
}
#[must_use]
pub const fn with_max_age(mut self, age: Duration) -> Self {
self.max_age = age;
self
}
#[must_use]
pub const fn with_unreachable_retries(mut self, retries: u32) -> Self {
self.retries = retries;
self
}
#[must_use]
pub fn with_sitemaps(mut self, tag: impl Into<Tag>) -> Self {
self.sitemaps = Some(tag.into());
self
}
pub(crate) fn retry_delay(&self, attempt: u32) -> Duration {
let exponent = attempt.saturating_sub(1).min(16);
self.retry_delay
.saturating_mul(1 << exponent)
.min(Duration::from_mins(10))
}
}
impl From<&str> for Robots {
fn from(user_agent: &str) -> Self {
Self::new(user_agent)
}
}
impl From<String> for Robots {
fn from(user_agent: String) -> Self {
Self::new(user_agent)
}
}
impl fmt::Debug for Robots {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Robots")
.field("agent", &self.agent)
.field("own_backend", &self.source.is_some())
.field("max_age", &self.max_age)
.field("retries", &self.retries)
.field("retry_delay", &self.retry_delay)
.field("sitemaps", &self.sitemaps)
.finish()
}
}