kyyn-core 0.1.15

Core vocabulary for kyyn: registry, links, query AST, plugin and validation contracts for typed, git-backed knowledge bases.
Documentation
//! Compiler-backed schema authoring: small traits generated by the
//! `KyynType`/`KyynKind` derives, plus the assembly macro that turns an
//! explicit kind inventory into the existing [`crate::registry::Registry`].
//!
//! This is an authoring facade, not another schema representation. Every
//! generated value uses the same registry vocabulary handwritten schema
//! crates use, and manual trait implementations are the escape hatch.

use crate::registry::{FieldType, Kind};
use serde::{Serialize, de::DeserializeOwned};

pub use crate::schemadef::toned;

/// A Rust type that has one unambiguous representation in a registry field.
pub trait KyynType: Serialize + DeserializeOwned {
    fn field_type() -> FieldType;
}

/// A stored record kind. Implemented by `#[derive(KyynKind)]` or manually for
/// representations outside the derive's deliberately small surface.
pub trait KyynKind: Serialize + DeserializeOwned {
    fn kind() -> Kind;
}

impl KyynType for String {
    fn field_type() -> FieldType {
        FieldType::Str
    }
}

impl KyynType for i64 {
    fn field_type() -> FieldType {
        FieldType::Int
    }
}

impl KyynType for bool {
    fn field_type() -> FieldType {
        FieldType::Bool
    }
}

impl KyynType for chrono::NaiveDate {
    fn field_type() -> FieldType {
        FieldType::Date
    }
}

impl<T: KyynType> KyynType for Option<T> {
    fn field_type() -> FieldType {
        FieldType::Option(Box::new(T::field_type()))
    }
}

impl<T: KyynType> KyynType for Vec<T> {
    fn field_type() -> FieldType {
        FieldType::List(Box::new(T::field_type()))
    }
}

/// Variants from a derived enum, for a badge role declaration. Panics only
/// when a schema author asks a non-enum type for variants.
pub fn enum_variants<T: KyynType>() -> Vec<crate::registry::Variant> {
    match T::field_type() {
        FieldType::Enum(variants) => variants,
        other => panic!("expected enum KyynType, got {other:?}"),
    }
}

/// Assemble a schema crate around an explicit kind inventory.
///
/// The generated `validate_entries_with` ALWAYS runs registry validation;
/// `custom_validate` can only append findings. Omitting the optional hook is
/// the ordinary schema with no bespoke invariants.
#[macro_export]
macro_rules! kyyn_schema {
    (
        kinds: [$($kind:ty),* $(,)?],
        roles: $roles:expr,
        queries: $queries:expr
        $(, custom_validate: $custom:path)?
        $(,)?
    ) => {
        $crate::kyyn_schema! {
            @impl
            schema_hash: env!("SCHEMA_SRC_HASH"),
            kinds: [$($kind),*],
            roles: $roles,
            queries: $queries
            $(, custom_validate: $custom)?
        }
    };
    (
        #[doc(hidden)] schema_hash: $schema_hash:expr,
        kinds: [$($kind:ty),* $(,)?],
        roles: $roles:expr,
        queries: $queries:expr
        $(, custom_validate: $custom:path)?
        $(,)?
    ) => {
        $crate::kyyn_schema! {
            @impl
            schema_hash: $schema_hash,
            kinds: [$($kind),*],
            roles: $roles,
            queries: $queries
            $(, custom_validate: $custom)?
        }
    };
    (
        @impl
        schema_hash: $schema_hash:expr,
        kinds: [$($kind:ty),* $(,)?],
        roles: $roles:expr,
        queries: $queries:expr
        $(, custom_validate: $custom:path)?
        $(,)?
    ) => {
        /// Hash of this crate's schema-bearing sources, supplied by its build
        /// script and checked against committed `registry.ron` at accept.
        pub fn schema_hash() -> String {
            $schema_hash.to_string()
        }

        /// The generated engine-facing registry projection.
        pub fn registry() -> $crate::registry::Registry {
            $crate::registry::Registry {
                schema_hash: schema_hash(),
                kinds: vec![$(<$kind as $crate::schema::KyynKind>::kind()),*],
                roles: $roles,
                queries: $queries,
            }
        }

        pub fn registry_ron() -> String {
            $crate::ronfmt::to_ron(&registry()).expect("registry serializes")
        }

        /// Generic structural validation is non-optional. A custom hook may
        /// add domain findings but cannot replace or suppress this baseline.
        pub fn validate_entries_with(
            entries: &[(String, String)],
            systems: &[&str],
        ) -> Vec<$crate::violation::Violation> {
            let mut out = $crate::validate::against_registry(&registry(), entries, systems);
            $(out.extend($custom(entries, systems));)?
            out
        }

        /// Serve the standard schema subprocess protocol.
        pub fn serve() {
            $crate::protocol::serve_schema(registry, validate_entries_with);
        }
    };
}