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 fn admitted_concurrency(&self) -> Option<usize> {
85 None
86 }
87}
88
89pub trait CrawlSink: Send + Sync {
91 fn page(&self, doc: Document) -> BoxFuture<'_, ()>;
92 fn error<'a>(&'a self, url: &'a str, err: ApiError) -> BoxFuture<'a, ()>;
93 fn total(&self, total: usize) -> BoxFuture<'_, ()>;
95 fn cancelled(&self) -> BoxFuture<'_, bool>;
96
97 fn resume(&self) -> Option<CrawlResume> {
100 None
101 }
102
103 fn tracks_frontier(&self) -> bool {
105 false
106 }
107
108 fn enqueued(&self, _urls: Vec<(String, usize)>) -> BoxFuture<'_, ()> {
110 Box::pin(async {})
111 }
112
113 fn summary(&self, _summary: CrawlSummary) -> BoxFuture<'_, ()> {
115 Box::pin(async {})
116 }
117}
118
119#[derive(Debug, Clone, Default, PartialEq)]
121pub struct CrawlResume {
122 pub enqueued: Vec<String>,
124 pub pending: Vec<(String, usize)>,
126 pub done: usize,
128}