use std::collections::VecDeque;
use std::sync::Arc;
use corpora_core::{Config, Context, Event, RunSummary, Severity, Subscriber};
pub struct Engine {
subs: Vec<Box<dyn Subscriber>>,
cfg: Arc<Config>,
}
impl Engine {
pub fn new(cfg: Config) -> Self {
Engine {
subs: Vec::new(),
cfg: Arc::new(cfg),
}
}
pub fn register(&mut self, s: impl Subscriber + 'static) -> &mut Self {
self.subs.push(Box::new(s));
self
}
pub fn run(&mut self, seed: Event) -> RunSummary {
let cfg = self.cfg.clone();
let mut queue: VecDeque<Event> = VecDeque::from([seed]);
let mut errors = 0usize;
let mut warnings = 0usize;
let mut settled = false;
loop {
let ev = match queue.pop_front() {
Some(ev) => ev,
None if !settled => {
settled = true;
Event::Settled
}
None => break,
};
if let Event::Diagnostic(d) = &ev {
match d.severity {
Severity::Error => errors += 1,
Severity::Warning => warnings += 1,
}
}
let mut out: VecDeque<Event> = VecDeque::new();
for s in self.subs.iter_mut() {
if s.interest().wants(&ev) {
let mut cx = Context::new(&cfg, &mut out);
s.handle(&ev, &mut cx);
}
}
queue.extend(out);
}
RunSummary { errors, warnings }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph_builder::GraphBuilder;
use crate::source::{Source, SourceDriver, VecSource};
use corpora_core::subscriber::ek;
use corpora_core::*;
fn rec(id: &str, path: &str) -> Record {
Record::minimal(
Some(Id(id.into())),
DocPath(path.into()),
Kind::Decision,
Lifecycle::Current,
Authority::Normative,
Facet::Narrative,
)
}
#[test]
fn pipeline_runs_and_tallies_graph_diagnostics() {
let recs = vec![Arc::new(rec("D1", "a.md")), Arc::new(rec("D1", "b.md"))];
let mut e = Engine::new(Config::new("."));
e.register(SourceDriver::new(Box::new(VecSource::new(recs))))
.register(GraphBuilder::new());
let s = e.run(Event::RunStarted {
run: RunId(1),
mode: Mode::Check,
});
assert_eq!(s.errors, 1);
}
struct DiscoverSource(Vec<&'static str>);
impl Source for DiscoverSource {
fn scan(&mut self, cx: &mut Context<'_>) {
for p in &self.0 {
cx.emit(Event::Discovered(DocPath((*p).into())));
}
}
}
struct DeferredParser;
impl Subscriber for DeferredParser {
fn name(&self) -> &'static str {
"deferred-parser"
}
fn interest(&self) -> Interest {
Interest::only(ek::DISCOVERED)
}
fn handle(&mut self, ev: &Event, cx: &mut Context<'_>) {
if let Event::Discovered(p) = ev {
let r = Record::minimal(
Some(Id("D1".into())),
p.clone(),
Kind::Decision,
Lifecycle::Current,
Authority::Normative,
Facet::Narrative,
);
cx.emit(Event::Parsed(Arc::new(r)));
}
}
}
#[test]
fn settled_waits_for_deferred_parses() {
let mut e = Engine::new(Config::new("."));
e.register(SourceDriver::new(Box::new(DiscoverSource(vec!["a.md", "b.md"]))))
.register(DeferredParser)
.register(GraphBuilder::new());
let s = e.run(Event::RunStarted {
run: RunId(1),
mode: Mode::Check,
});
assert_eq!(s.errors, 1, "both deferred-parsed records must reach the graph");
}
}