use std::fs;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use corpora_core::subscriber::ek;
use corpora_core::{Context, DocPath, Event, Interest, Record, Subscriber};
pub trait Source {
fn scan(&mut self, cx: &mut Context<'_>);
}
pub struct SourceDriver {
inner: Box<dyn Source>,
}
impl SourceDriver {
pub fn new(inner: Box<dyn Source>) -> Self {
SourceDriver { inner }
}
}
impl Subscriber for SourceDriver {
fn name(&self) -> &'static str {
"source"
}
fn interest(&self) -> Interest {
Interest::only(ek::RUN_STARTED | ek::CHANGED)
}
fn handle(&mut self, ev: &Event, cx: &mut Context<'_>) {
match ev {
Event::RunStarted { .. } => self.inner.scan(cx),
Event::Changed(p) => cx.emit(Event::Discovered(p.clone())),
_ => {}
}
}
}
pub struct FsWalk {
root: PathBuf,
}
impl FsWalk {
pub fn new(root: impl Into<PathBuf>) -> Self {
FsWalk { root: root.into() }
}
}
impl Source for FsWalk {
fn scan(&mut self, cx: &mut Context<'_>) {
let mut paths = Vec::new();
walk(&self.root, &mut paths);
paths.sort(); for p in paths {
cx.emit(Event::Discovered(DocPath(p)));
}
}
}
fn walk(dir: &Path, out: &mut Vec<String>) {
let Ok(entries) = fs::read_dir(dir) else {
return;
};
for e in entries.flatten() {
let path = e.path();
if path.is_dir() {
walk(&path, out);
} else if path.extension().and_then(|x| x.to_str()) == Some("md") {
if let Some(s) = path.to_str() {
out.push(s.to_string());
}
}
}
}
pub struct VecSource {
records: Vec<Arc<Record>>,
}
impl VecSource {
pub fn new(records: Vec<Arc<Record>>) -> Self {
VecSource { records }
}
}
impl Source for VecSource {
fn scan(&mut self, cx: &mut Context<'_>) {
for r in &self.records {
cx.emit(Event::Parsed(r.clone()));
}
}
}