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, Upload,
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 parse<'a>(
26 &'a self,
27 _file: Upload,
28 _opts: &'a ScrapeOptions,
29 ) -> BoxFuture<'a, Result<Document, ApiError>> {
30 Box::pin(async {
31 Err(ApiError::unsupported(
32 "parse is not available in this build",
33 ))
34 })
35 }
36
37 fn map<'a>(&'a self, _req: &'a MapRequest) -> BoxFuture<'a, Result<Vec<MapLink>, ApiError>> {
38 Box::pin(async { Err(ApiError::unsupported("map is not available in this build")) })
39 }
40
41 fn crawl<'a>(
43 &'a self,
44 _req: &'a CrawlRequest,
45 _sink: Arc<dyn CrawlSink>,
46 ) -> BoxFuture<'a, Result<(), ApiError>> {
47 Box::pin(async {
48 Err(ApiError::unsupported(
49 "crawl is not available in this build",
50 ))
51 })
52 }
53
54 fn search<'a>(&'a self, _req: &'a SearchRequest) -> BoxFuture<'a, Result<Value, ApiError>> {
56 Box::pin(async {
57 Err(ApiError::unsupported(
58 "search is not configured: set [search] in pagesift.toml",
59 ))
60 })
61 }
62
63 fn extract<'a>(&'a self, _req: &'a ExtractRequest) -> BoxFuture<'a, Result<Value, ApiError>> {
65 Box::pin(async {
66 Err(ApiError::unsupported(
67 "extract needs an LLM: set [llm] in pagesift.toml",
68 ))
69 })
70 }
71
72 fn supported_formats(&self) -> &'static [&'static str] {
74 &["markdown", "html", "rawHtml", "links", "images"]
75 }
76
77 fn supports_actions(&self) -> bool {
79 false
80 }
81}
82
83pub trait CrawlSink: Send + Sync {
85 fn page(&self, doc: Document) -> BoxFuture<'_, ()>;
86 fn error<'a>(&'a self, url: &'a str, err: ApiError) -> BoxFuture<'a, ()>;
87 fn total(&self, total: usize) -> BoxFuture<'_, ()>;
89 fn cancelled(&self) -> BoxFuture<'_, bool>;
90
91 fn resume(&self) -> Option<CrawlResume> {
94 None
95 }
96
97 fn tracks_frontier(&self) -> bool {
99 false
100 }
101
102 fn enqueued(&self, _urls: Vec<(String, usize)>) -> BoxFuture<'_, ()> {
104 Box::pin(async {})
105 }
106
107 fn summary(&self, _summary: CrawlSummary) -> BoxFuture<'_, ()> {
109 Box::pin(async {})
110 }
111}
112
113#[derive(Debug, Clone, Default, PartialEq)]
115pub struct CrawlResume {
116 pub enqueued: Vec<String>,
118 pub pending: Vec<(String, usize)>,
120 pub done: usize,
122}