verit-core 0.2.0

Internal: portable core engine for Exavian Veritate. Not a public API — depend on `verit`.
Documentation
//! Runtime support for `#[derive(Verit)]` — the Rust peer of Python's `@verit`
//! decorator.
//!
//! The `verit-derive` proc-macro generates an impl of [`VeritType`] for a
//! struct: it builds the struct's [`Schema`] from the field types (and every
//! nested `#[derive(Verit)]` type it references), then rides the dynamic
//! encoder and reader for `to_verit` / `from_verit`. The macro lives in a
//! separate, feature-gated crate so `verit`'s default build keeps **zero
//! dependencies** (syn/quote are build-only, off by default). This trait is
//! plain Rust and always present — it costs nothing when the macro is unused.
//!
//! The wire contract is the schema's 128-bit content id: a Rust `#[derive(Verit)]`
//! type, a Python `@verit` class, and a hand-written `.vsc` IDL that describe
//! the same fields all produce the same id, so their bytes interoperate.

use std::collections::BTreeSet;

use crate::encode::{encode, SchemaMode};
use crate::message::{Message, StructReader};
use crate::resolve::Resolver;
use crate::schema::{Schema, SchemaBuilder, StructMode};
use crate::value::Value;
use crate::Result;

/// Implemented by every `#[derive(Verit)]` type. All of it is generated; you
/// never write an impl by hand. The provided methods ([`to_verit`],
/// [`from_verit`], [`verit_schema_id`]) are the surface you actually call.
///
/// [`to_verit`]: VeritType::to_verit
/// [`from_verit`]: VeritType::from_verit
/// [`verit_schema_id`]: VeritType::verit_schema_id
pub trait VeritType: Sized {
    /// This type's name in the generated schema (its Rust type name).
    const VERIT_NAME: &'static str;

    /// How this struct is stored on the wire (`#[verit(mode = "…")]`).
    const VERIT_MODE: StructMode;

    /// Add this type's struct definition — and, transitively, every nested
    /// `VeritType` it references — to `builder`, skipping any name already in
    /// `seen`. The derive generates this; it is the mechanism that lets one
    /// root type pull its whole type graph into a single schema.
    fn verit_register(builder: SchemaBuilder, seen: &mut BTreeSet<&'static str>) -> SchemaBuilder;

    /// Pack `self` into a dynamic struct [`Value`] (the encoder's input form).
    fn verit_pack(&self) -> Value;

    /// Reconstruct `Self` from a dynamic [`StructReader`] over this type's
    /// fields.
    fn verit_unpack(reader: &StructReader) -> Result<Self>;

    /// The process-wide [`Schema`] rooted at this type, built once and cached.
    /// Generated with a per-type `OnceLock`, so the schema (and its 128-bit id)
    /// is computed at most once per program run.
    fn verit_schema() -> &'static Schema;

    /// This type's 128-bit schema id — the cross-language wire contract.
    fn verit_schema_id() -> u128 {
        Self::verit_schema().id()
    }

    /// Encode `self` to Veritate bytes against this type's schema.
    /// `SchemaMode::Inline` embeds the schema (self-describing, `verit dump`-able);
    /// `SchemaMode::HashOnly` writes just the 128-bit id.
    fn to_verit(&self, mode: SchemaMode) -> Result<Vec<u8>> {
        encode(Self::verit_schema(), &self.verit_pack(), mode)
    }

    /// Decode `Self` from bytes written against this same type's schema
    /// (matching the Python decorator's identity-resolver behaviour). To read
    /// bytes written by an *evolved* schema, resolve explicitly with
    /// [`Resolver::new`] and call [`verit_unpack`](VeritType::verit_unpack).
    fn from_verit(bytes: &[u8]) -> Result<Self> {
        let schema = Self::verit_schema();
        let resolver = Resolver::identity(schema)?;
        let msg = Message::parse(bytes)?;
        let root = msg.root(&resolver)?;
        Self::verit_unpack(&root)
    }
}