corpora-rules 0.1.0

Validation rule pack (E3, held-decision, schema) for the corpora docs validator.
Documentation
//! Plugin seam #1 — self-validation. A [`Rule`] is pure `&Graph -> diagnostics`;
//! [`RuleRunner`] fires the registered pack on every `GraphBuilt`.

use corpora_core::subscriber::ek;
use corpora_core::{Context, Diagnostic, Event, Graph, Interest, RevisionOracle, Subscriber};

pub mod e3;
pub mod gate_b;
pub mod held;
pub mod positional;
pub mod schema;
pub mod supersession;

pub use e3::E3;
pub use gate_b::GateB;
pub use held::HeldDecision;
pub use positional::PositionalRef;
pub use schema::Schema;
pub use supersession::SupersessionIntegrity;

pub trait Rule {
    fn code(&self) -> &'static str;
    fn check(&self, g: &Graph, out: &mut Vec<Diagnostic>);
}

pub struct RuleRunner {
    rules: Vec<Box<dyn Rule>>,
}

impl RuleRunner {
    pub fn with(rules: Vec<Box<dyn Rule>>) -> Self {
        RuleRunner { rules }
    }
}

impl Subscriber for RuleRunner {
    fn name(&self) -> &'static str {
        "rules"
    }

    fn interest(&self) -> Interest {
        Interest::only(ek::GRAPH_BUILT)
    }

    fn handle(&mut self, ev: &Event, cx: &mut Context<'_>) {
        if let Event::GraphBuilt(g) = ev {
            let mut ds = Vec::new();
            for r in &self.rules {
                r.check(g, &mut ds);
            }
            for d in ds {
                cx.error(d);
            }
        }
    }
}

/// The default rule pack. `GateB` needs a [`RevisionOracle`] (git); pass a `NullOracle`
/// to run the pure rules without VCS context.
pub fn default_rules(oracle: Box<dyn RevisionOracle>) -> Vec<Box<dyn Rule>> {
    vec![
        Box::new(Schema),
        Box::new(SupersessionIntegrity),
        Box::new(E3),
        Box::new(HeldDecision),
        Box::new(PositionalRef),
        Box::new(GateB::new(oracle)),
    ]
}