pub(crate) mod dynamic;
pub mod facade;
mod memory;
pub mod queue;
pub mod score;
pub mod seed;
pub mod seen;
#[cfg(test)]
pub(crate) mod test_util;
use std::future::{Future, ready};
use std::time::Duration;
use tokio::time::Instant;
use self::facade::{
AllowedHosts, AvoidTraps, Dedup, Filter, MaxDepth, Retry, RetryPolicy, Revisits, Scored,
TagBackoff, TrapPolicy,
};
pub use self::memory::InMemFrontier;
use self::score::Score;
use self::seed::Seed;
use self::seen::{InMemSeen, SeenSet};
use crate::Result;
use crate::crawler::{Revisit, Tag};
pub trait Frontier: Send + Sync + 'static {
fn push(&self, seed: Seed) -> impl Future<Output = Result<Admission>> + Send;
fn pop(&self) -> impl Future<Output = Result<Pop>> + Send;
fn report(&self, report: Report) -> impl Future<Output = Result<Ack>> + Send {
let ack = match report {
Report::Retry { .. } => Ack::RetryRefused,
_ => Ack::Applied,
};
ready(Ok(ack))
}
fn size_hint(&self) -> Option<usize> {
None
}
fn snapshot(&self) -> Option<Vec<Seed>> {
None
}
}
pub trait Schedule: Frontier {
fn schedule(&self, seed: Seed, due: Instant) -> impl Future<Output = Result<()>> + Send;
fn next_due(&self) -> impl Future<Output = Result<Option<Instant>>> + Send;
}
impl<F: Frontier> Frontier for std::sync::Arc<F> {
fn push(&self, seed: Seed) -> impl Future<Output = Result<Admission>> + Send {
F::push(self, seed)
}
fn pop(&self) -> impl Future<Output = Result<Pop>> + Send {
F::pop(self)
}
fn report(&self, report: Report) -> impl Future<Output = Result<Ack>> + Send {
F::report(self, report)
}
fn size_hint(&self) -> Option<usize> {
F::size_hint(self)
}
fn snapshot(&self) -> Option<Vec<Seed>> {
F::snapshot(self)
}
}
impl<F: Schedule> Schedule for std::sync::Arc<F> {
fn schedule(&self, seed: Seed, due: Instant) -> impl Future<Output = Result<()>> + Send {
F::schedule(self, seed, due)
}
fn next_due(&self) -> impl Future<Output = Result<Option<Instant>>> + Send {
F::next_due(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Admission {
Queued,
Duplicate,
TooDeep,
Unsupported,
OutOfScope,
SuspectedTrap,
PrefixBudgetExceeded,
}
impl Admission {
#[must_use]
pub const fn is_queued(self) -> bool {
matches!(self, Self::Queued)
}
}
#[derive(Debug)]
pub enum Pop {
Ready(Seed),
Wait(Instant),
Empty,
}
#[derive(Debug)]
#[non_exhaustive]
pub enum Report {
Done(Seed),
Failed(Seed),
Skipped(Seed),
Cancelled(Seed),
Retry {
seed: Seed,
after: Option<Duration>,
},
Backoff {
tag: Option<Tag>,
until: Instant,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Ack {
Applied,
RetryRefused,
}
pub trait FrontierLayer<F> {
type Frontier: Frontier;
fn layer(self, inner: F) -> Self::Frontier;
}
pub trait FrontierExt: Frontier + Sized {
fn dedup(self) -> Dedup<Self> {
Dedup::new(self, InMemSeen::default())
}
fn dedup_with<S: SeenSet>(self, seen: S) -> Dedup<Self, S> {
Dedup::new(self, seen)
}
fn filter<P>(self, predicate: P) -> Filter<Self, P>
where
P: Fn(&Seed) -> bool + Send + Sync + 'static,
{
Filter::new(self, predicate)
}
fn allow_hosts<I>(
self,
hosts: I,
) -> Filter<Self, impl Fn(&Seed) -> bool + Send + Sync + 'static>
where
I: IntoIterator<Item: AsRef<str>>,
{
let hosts = AllowedHosts::new(hosts);
Filter::new(self, move |seed: &Seed| hosts.allows(seed))
}
fn max_depth(self, depth: u32) -> MaxDepth<Self> {
MaxDepth::new(self, depth)
}
fn avoid_traps(self) -> AvoidTraps<Self> {
self.avoid_traps_with(TrapPolicy::default())
}
fn avoid_traps_with(self, policy: TrapPolicy) -> AvoidTraps<Self> {
AvoidTraps::new(self, policy)
}
fn retry(self, max_retries: u32) -> Retry<Self> {
Retry::new(self, RetryPolicy::new(max_retries))
}
fn retry_with(self, policy: RetryPolicy) -> Retry<Self> {
Retry::new(self, policy)
}
fn tag_backoff(self) -> TagBackoff<Self> {
TagBackoff::new(self)
}
fn revisit<T: Revisit>(self) -> Revisits<Self, T>
where
Self: Schedule,
{
Revisits::new(self)
}
fn score<S: Score>(self, scorer: S) -> Scored<Self, S> {
Scored::new(self, scorer)
}
fn layer<L: FrontierLayer<Self>>(self, layer: L) -> L::Frontier {
layer.layer(self)
}
}
impl<F: Frontier> FrontierExt for F {}