verit 0.1.0

Exavian Veritate — zero-copy, self-describing, schema-evolvable binary serialization, safe on untrusted bytes, no unsafe, byte-identical across independent implementations.
Documentation
//! Reference integration — a tiny append-only **event store** that dogfoods the
//! whole Veritate enterprise surface end to end:
//!
//! - **`#[derive(Verit)]`** defines the event types (no hand-written schema).
//! - A **`SchemaRegistry`** + distribution bundle carries schemas between the
//!   producer and consumer "services".
//! - A **`.vertc` container** persists a batch of events at rest, read back
//!   **zero-copy** from the file's bytes.
//! - **Schema evolution** is exercised: a legacy hash-only event (an older
//!   schema) is still read, resolved through the registry into today's type.
//!
//! Run it (the derive feature is needed for `#[derive(Verit)]`):
//!
//! ```text
//! cargo run -p verit --features derive --example eventstore
//! ```
//!
//! It prints what each step does and `assert!`s the round-trips, so it doubles
//! as an executable integration test.

use verit::{
    encode, Container, ContainerWriter, Dt, Message, SchemaBuilder, SchemaMode, SchemaRegistry,
    Value, Verit, VeritType,
};

#[derive(Verit, Debug, Clone, Copy, PartialEq)]
#[verit(mode = "dense")]
struct Coord {
    #[verit(id = 1)]
    lat: f64,
    #[verit(id = 2)]
    lon: f64,
}

/// Today's event schema (v2): `at` and `region` were added over the years.
#[derive(Verit, Debug, Clone, PartialEq)]
struct Event {
    #[verit(id = 1)]
    seq: u64,
    #[verit(id = 2)]
    kind: String,
    #[verit(id = 3)]
    payload: String,
    #[verit(id = 4)]
    at: Option<Coord>,
    #[verit(id = 5)]
    tags: Vec<String>,
    #[verit(id = 6)]
    region: Option<String>,
}

fn main() {
    println!("== Veritate event store — reference integration ==\n");

    // ---- Producer service: append events, persist to a container --------------
    let events = vec![
        Event {
            seq: 1,
            kind: "login".into(),
            payload: "user=alice".into(),
            at: Some(Coord {
                lat: 51.5,
                lon: -0.12,
            }),
            tags: vec!["auth".into()],
            region: Some("eu-west".into()),
        },
        Event {
            seq: 2,
            kind: "purchase".into(),
            payload: "sku=42 qty=3".into(),
            at: None,
            tags: vec!["billing".into(), "priority".into()],
            region: None,
        },
    ];

    // Each event is encoded hash-only (compact wire: just the 128-bit id, no
    // inline schema) and appended as a container record.
    let mut writer = ContainerWriter::new();
    for e in &events {
        writer.add(&e.to_verit(SchemaMode::HashOnly).unwrap());
    }
    let file_bytes = writer.finish();
    println!(
        "producer: wrote {} events → {}-byte .vertc container (hash-only records)",
        events.len(),
        file_bytes.len()
    );

    // The producer publishes its schema for consumers to fetch. In a real
    // deployment this bundle travels over HTTP / object storage; here it is just
    // bytes handed to the consumer.
    let mut producer_registry = SchemaRegistry::new();
    producer_registry.register(Event::verit_schema().clone());
    let bundle = producer_registry.to_bundle();
    println!(
        "producer: published schema {:032x} in a {}-byte distribution bundle\n",
        Event::verit_schema_id(),
        bundle.len()
    );

    // ---- Consumer service: load registry + container, read zero-copy ----------
    let consumer_registry = SchemaRegistry::from_bundle(&bundle).unwrap();
    println!(
        "consumer: loaded {} schema(s) from the bundle",
        consumer_registry.len()
    );

    // `mmap` in the real world; `&file_bytes` here. Records are read in place.
    let container = Container::parse(&file_bytes).unwrap();
    println!("consumer: opened container, {} records", container.len());

    for (i, (record, original)) in container.iter().zip(&events).enumerate() {
        let back = Event::from_verit(record).unwrap();
        println!(
            "  [{i}] seq={} kind={} tags={:?}",
            back.seq, back.kind, back.tags
        );
        assert_eq!(&back, original, "record {i} round-trips");
    }
    println!();

    // ---- Schema evolution: read a legacy peer's hash-only event ---------------
    // An old service (v1) never knew about `at`/`tags`/`region` and used a
    // narrower `seq`. Its message carries only the schema id on the wire.
    let legacy = SchemaBuilder::new()
        .add_struct(
            "Event",
            vec![
                (1, "seq", Dt::U32), // widens to today's u64
                (2, "kind", Dt::Str),
                (3, "payload", Dt::Str),
            ],
        )
        .build("Event")
        .unwrap();
    let legacy_msg = encode(
        &legacy,
        &Value::Struct(vec![
            (1, Value::U32(99)),
            (2, Value::str("healthcheck")),
            (3, Value::str("ok")),
        ]),
        SchemaMode::HashOnly,
    )
    .unwrap();

    // The consumer registers the legacy schema (e.g. fetched by id on cache
    // miss), then resolves the legacy id into today's `Event` reader schema.
    let mut consumer_registry = consumer_registry;
    let legacy_id = consumer_registry.register(legacy);
    let msg = Message::parse(&legacy_msg).unwrap();
    let resolver = consumer_registry
        .resolver_for(msg.schema_id(), Event::verit_schema())
        .unwrap();
    let root = msg.root(&resolver).unwrap();

    println!("consumer: read a legacy hash-only event via the registry:");
    println!(
        "  seq={:?} kind={:?} at={:?} (fields added since are absent)",
        root.get_u64(1).unwrap(),
        root.get_str(2).unwrap(),
        root.get_struct(4).unwrap().map(|_| "present"),
    );
    assert_eq!(root.get_u64(1).unwrap(), Some(99), "u32 seq widened to u64");
    assert_eq!(root.get_str(2).unwrap(), Some("healthcheck"));
    assert_eq!(legacy_id, msg.schema_id());
    assert!(
        root.get_struct(4).unwrap().is_none(),
        "`at` absent in legacy data"
    );

    println!("\n== all round-trips and the evolution read passed ==");
}