use pagesift_api::types::{Document, ScrapeOptions};
use pagesift_api::{ApiError, ErrorCode};
use pagesift_hooks::{Request, Verdict};
use super::{Scraper, Source};
impl Scraper {
pub(super) async fn scrape_inner(
&self,
source: Source<'_>,
opts: &ScrapeOptions,
) -> Result<Document, ApiError> {
if self.hooks.is_empty() {
return self.scrape_plain(source, opts).await;
}
let (url, mode) = match source {
Source::Url(url, mode) => (url.to_string(), mode),
Source::Upload(_) => {
let doc = Box::pin(self.scrape_plain(source, opts)).await?;
let name = doc.metadata.source_url.clone().unwrap_or_default();
return Ok(self.hooks.run_document(&name, doc));
}
};
let req = self
.hooks
.run_request(Request {
url,
options: opts.clone(),
})
.map_err(|why| {
ApiError::new(
ErrorCode::UnsupportedOption,
format!("skipped by a request hook: {why}"),
)
})?;
let mut doc =
Box::pin(self.scrape_plain(Source::Url(&req.url, mode), &req.options)).await?;
match self.hooks.run_response(&req.url, &doc) {
Verdict::Keep => {}
Verdict::Retry => {
doc =
Box::pin(self.scrape_plain(Source::Url(&req.url, mode), &req.options)).await?;
}
Verdict::Drop(why) => {
return Err(ApiError::new(
ErrorCode::UpstreamError,
format!("dropped by a response hook: {why}"),
));
}
}
Ok(self.hooks.run_document(&req.url, doc))
}
}
pub struct ScraperBuilder {
config: super::ScraperConfig,
}
impl Scraper {
pub fn builder(config: super::ScraperConfig) -> ScraperBuilder {
ScraperBuilder { config }
}
pub fn hooks(&self) -> &pagesift_hooks::Hooks {
&self.hooks
}
}
impl ScraperBuilder {
pub fn hook(mut self, hooks: &pagesift_hooks::Hooks) -> Self {
self.config.hooks.extend(hooks);
self
}
pub fn build(self) -> Result<Scraper, super::ScraperError> {
Scraper::new(self.config)
}
}