use serde::{Deserialize, Serialize};
use url::Url;
use crate::crawl::{RobotsFile, SitemapSummary};
use crate::page::PageRecord;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Edge {
pub from: u32,
pub to: u32,
pub anchor: u32,
pub nofollow: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LinkGraph {
pub edges: Vec<Edge>,
pub anchors: Vec<String>,
}
impl LinkGraph {
pub fn anchor(&self, e: &Edge) -> &str {
&self.anchors[e.anchor as usize]
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "kind", content = "reason")]
pub enum StopReason {
Completed,
PageLimit,
TimeLimit,
Unreachable(String),
Blocked(String),
RobotsBlocked,
}
impl StopReason {
pub fn is_complete(&self) -> bool {
matches!(self, StopReason::Completed)
}
pub fn crawl_ran(&self) -> bool {
matches!(
self,
StopReason::Completed | StopReason::PageLimit | StopReason::TimeLimit
)
}
}
pub const UNREACHABLE_REASON_PREFIX: &str = "site unreachable";
pub const BLOCKED_REASON_PREFIX: &str = "site blocked our crawler";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SiteFault {
Unreachable,
Blocked,
}
impl SiteFault {
pub fn from_failure_reason(reason: &str) -> Option<SiteFault> {
if reason.starts_with(UNREACHABLE_REASON_PREFIX) {
Some(SiteFault::Unreachable)
} else if reason.starts_with(BLOCKED_REASON_PREFIX) {
Some(SiteFault::Blocked)
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct Progress {
pub pages_done: u32,
pub queued: u32,
pub failures: u32,
pub depth: u16,
pub elapsed_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CrawlOutput {
pub origin: Url,
pub pages: Vec<PageRecord>,
pub links: LinkGraph,
pub robots: Option<RobotsFile>,
pub sitemap: SitemapSummary,
pub stop: StopReason,
pub duration_ms: u64,
}