1use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use crate::error::ApiError;
8use crate::types::{
9 CrawlRequest, CrawlSummary, Document, ExtractRequest, MapLink, MapRequest, ScrapeOptions,
10 SearchRequest,
11};
12use serde_json::Value;
13
14pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
15
16pub trait Engine: Send + Sync + 'static {
17 fn scrape<'a>(
18 &'a self,
19 url: &'a str,
20 opts: &'a ScrapeOptions,
21 ) -> BoxFuture<'a, Result<Document, ApiError>>;
22
23 fn map<'a>(&'a self, _req: &'a MapRequest) -> BoxFuture<'a, Result<Vec<MapLink>, ApiError>> {
24 Box::pin(async { Err(ApiError::unsupported("map is not available in this build")) })
25 }
26
27 fn crawl<'a>(
29 &'a self,
30 _req: &'a CrawlRequest,
31 _sink: Arc<dyn CrawlSink>,
32 ) -> BoxFuture<'a, Result<(), ApiError>> {
33 Box::pin(async {
34 Err(ApiError::unsupported(
35 "crawl is not available in this build",
36 ))
37 })
38 }
39
40 fn search<'a>(&'a self, _req: &'a SearchRequest) -> BoxFuture<'a, Result<Value, ApiError>> {
42 Box::pin(async {
43 Err(ApiError::unsupported(
44 "search is not configured: set [search] in pagesift.toml",
45 ))
46 })
47 }
48
49 fn extract<'a>(&'a self, _req: &'a ExtractRequest) -> BoxFuture<'a, Result<Value, ApiError>> {
51 Box::pin(async {
52 Err(ApiError::unsupported(
53 "extract needs an LLM: set [llm] in pagesift.toml",
54 ))
55 })
56 }
57
58 fn supported_formats(&self) -> &'static [&'static str] {
60 &["markdown", "html", "rawHtml", "links", "images"]
61 }
62
63 fn supports_actions(&self) -> bool {
65 false
66 }
67}
68
69pub trait CrawlSink: Send + Sync {
71 fn page(&self, doc: Document) -> BoxFuture<'_, ()>;
72 fn error<'a>(&'a self, url: &'a str, err: ApiError) -> BoxFuture<'a, ()>;
73 fn total(&self, total: usize) -> BoxFuture<'_, ()>;
75 fn cancelled(&self) -> BoxFuture<'_, bool>;
76
77 fn resume(&self) -> Option<CrawlResume> {
80 None
81 }
82
83 fn tracks_frontier(&self) -> bool {
85 false
86 }
87
88 fn enqueued(&self, _urls: Vec<(String, usize)>) -> BoxFuture<'_, ()> {
90 Box::pin(async {})
91 }
92
93 fn summary(&self, _summary: CrawlSummary) -> BoxFuture<'_, ()> {
95 Box::pin(async {})
96 }
97}
98
99#[derive(Debug, Clone, Default, PartialEq)]
101pub struct CrawlResume {
102 pub enqueued: Vec<String>,
104 pub pending: Vec<(String, usize)>,
106 pub done: usize,
108}