kyyn-core 0.1.11

Core vocabulary for kyyn: registry, links, query AST, plugin and validation contracts for typed, git-backed knowledge bases.
Documentation
//! The engine ⇄ schema-crate wire protocol: RON over stdio. The engine spawns
//! the KB's compiled schema binary, writes one `Request` to stdin, reads one
//! `Response` from stdout. Nothing else crosses the boundary — the engine
//! never links a schema crate.

use serde::{Deserialize, Serialize};

use crate::registry::Registry;
use crate::violation::Violation;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Request {
    /// Emit the registry (the schema's self-description).
    Registry,
    /// Validate a whole tree: (repo-relative path, RON text) pairs, plus the
    /// link system namespaces the tree's own `sources.ron` declares (the
    /// engine reads that manifest — schema crates never parse it).
    Validate {
        entries: Vec<(String, String)>,
        systems: Vec<String>,
    },
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Response {
    Registry(Registry),
    Violations(Vec<Violation>),
    /// The schema binary understood the request but could not serve it.
    Error(String),
}

/// The whole main() of a schema binary: one RON [`Request`] on stdin, one
/// RON [`Response`] on stdout. A schema crate supplies its registry and its
/// validation fn; everything else about the wire lives here.
pub fn serve_schema(
    registry: impl Fn() -> crate::registry::Registry,
    validate: impl Fn(&[(String, String)], &[&str]) -> Vec<crate::violation::Violation>,
) {
    use std::io::Read;
    let respond = |r: &Response| match crate::ronfmt::to_ron(r) {
        Ok(text) => print!("{text}"),
        Err(e) => {
            eprintln!("serializing response: {e}");
            std::process::exit(2);
        }
    };
    let mut input = String::new();
    if let Err(e) = std::io::stdin().read_to_string(&mut input) {
        respond(&Response::Error(format!("reading stdin: {e}")));
        std::process::exit(2);
    }
    let response = match ron::from_str::<Request>(&input) {
        Err(e) => Response::Error(format!("parsing request: {e}")),
        Ok(Request::Registry) => Response::Registry(registry()),
        Ok(Request::Validate { entries, systems }) => {
            let systems: Vec<&str> = systems.iter().map(String::as_str).collect();
            Response::Violations(validate(&entries, &systems))
        }
    };
    respond(&response);
}