use std::time::Duration;
use anyhow::{Context, Result};
use crawlberg::{CrawlConfig as KcCrawlConfig, CrawlEngineHandle, SsrfPolicy, create_engine};
use crate::config::CrawlConfig;
pub const WEB_MAP_URL_CAP: u32 = 1000;
fn kc_config(cfg: &CrawlConfig) -> Result<KcCrawlConfig> {
let max_pages = usize::try_from(cfg.max_pages).context("max_pages exceeds usize")?;
let max_depth = usize::try_from(cfg.max_depth).context("max_depth exceeds usize")?;
let max_body_size = usize::try_from(cfg.max_body_size).context("max_body_size exceeds usize")?;
Ok(KcCrawlConfig {
max_pages: Some(max_pages),
max_depth: Some(max_depth),
respect_robots_txt: cfg.respect_robots_txt,
user_agent: Some(cfg.user_agent.clone()),
max_body_size: Some(max_body_size),
max_concurrent: Some(4),
request_timeout: Duration::from_secs(30),
stay_on_domain: true,
map_limit: Some(WEB_MAP_URL_CAP as usize),
ssrf: SsrfPolicy {
deny_private: !cfg.allow_private_network,
..Default::default()
},
..Default::default()
})
}
pub fn build_engine(cfg: &CrawlConfig) -> Result<CrawlEngineHandle> {
if !cfg.respect_robots_txt {
tracing::warn!("crawl.respect_robots_txt is disabled — basemind will fetch URLs that robots.txt forbids");
}
create_engine(Some(kc_config(cfg)?)).context("create crawlberg engine")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn kc_config_stays_on_the_seed_host() {
let kc = kc_config(&CrawlConfig::default()).expect("default crawl config maps cleanly");
assert!(
kc.stay_on_domain,
"web_crawl must stay on the seed host — a crawl that follows off-host links is bug #34"
);
assert!(
!kc.allow_subdomains,
"same-host scope excludes subdomains unless a caller widens it explicitly"
);
assert_eq!(
kc.map_limit,
Some(WEB_MAP_URL_CAP as usize),
"web_map must bound the sitemap fetch to cap peak memory (crawlberg#33)"
);
}
}