verit 0.2.0

Exavian Veritate — zero-copy, self-describing, schema-evolvable binary serialization, safe on untrusted bytes, no unsafe, byte-identical across independent implementations.
Documentation
//! Custom-default conformance (the Rust reference for Phase 3). A field can
//! carry a scalar default in the schema; a reader synthesizes it via
//! `get_or_default` when the field is absent. Presence is untouched (so
//! transcoding stays byte-identical), and a schema without defaults is
//! byte-identical to the pre-defaults format.

use verit::{dump_json, encode, Dt, Message, Ref, Resolver, SchemaBuilder, SchemaMode, Value};

fn rec_schema() -> verit::Schema {
    SchemaBuilder::new()
        .add_struct("Rec", vec![(1, "n", Dt::U32), (2, "name", Dt::Str)])
        .set_default("Rec", 1, Value::U32(7))
        .build("Rec")
        .unwrap()
}

#[test]
fn absent_field_reads_its_default_but_get_stays_raw() {
    let schema = rec_schema();
    // Encode with `n` (id 1) absent.
    let bytes = encode(
        &schema,
        &Value::Struct(vec![(2, Value::str("x"))]),
        SchemaMode::Inline,
    )
    .unwrap();

    let resolver = Resolver::identity(&schema).unwrap();
    let msg = Message::parse(&bytes).unwrap();
    let root = msg.root(&resolver).unwrap();

    // Raw presence is unchanged: `get` still reports absent.
    assert_eq!(root.get_u32(1).unwrap(), None);
    assert!(root.get(1).unwrap().is_none());
    // `get_or_default` synthesizes the schema's default.
    assert!(matches!(root.get_or_default(1).unwrap(), Some(Ref::U32(7))));
    // A present field is returned as-is (default not used).
    assert!(matches!(
        root.get_or_default(2).unwrap(),
        Some(Ref::Str("x"))
    ));

    // dump shows the effective value (default filled in).
    assert_eq!(dump_json(&bytes).unwrap(), r#"{"n":7,"name":"x"}"#);
}

#[test]
fn present_value_overrides_the_default() {
    let schema = rec_schema();
    let bytes = encode(
        &schema,
        &Value::Struct(vec![(1, Value::U32(99)), (2, Value::str("x"))]),
        SchemaMode::Inline,
    )
    .unwrap();
    let resolver = Resolver::identity(&schema).unwrap();
    let msg = Message::parse(&bytes).unwrap();
    let root = msg.root(&resolver).unwrap();
    assert!(matches!(
        root.get_or_default(1).unwrap(),
        Some(Ref::U32(99))
    ));
    assert_eq!(dump_json(&bytes).unwrap(), r#"{"n":99,"name":"x"}"#);
}

#[test]
fn adding_a_default_changes_the_id_but_absence_of_one_does_not() {
    let with = rec_schema();
    let without = SchemaBuilder::new()
        .add_struct("Rec", vec![(1, "n", Dt::U32), (2, "name", Dt::Str)])
        .build("Rec")
        .unwrap();
    assert_ne!(
        with.id(),
        without.id(),
        "a default is part of the schema id"
    );

    // A schema with no defaults encodes byte-identically to the pre-defaults
    // format (no trailing section).
    let plain = SchemaBuilder::new()
        .add_struct("Rec", vec![(1, "n", Dt::U32), (2, "name", Dt::Str)])
        .build("Rec")
        .unwrap();
    assert_eq!(without.canonical_bytes(), plain.canonical_bytes());
}

#[test]
fn schema_with_defaults_round_trips_through_canonical() {
    let schema = rec_schema();
    let decoded = verit::Schema::from_canonical(schema.canonical_bytes()).unwrap();
    assert_eq!(decoded.id(), schema.id());
    assert_eq!(decoded.canonical_bytes(), schema.canonical_bytes());
}

#[test]
fn every_scalar_default_kind_round_trips() {
    let schema = SchemaBuilder::new()
        .add_enum("Color", vec![(0, "Red"), (1, "Green")])
        .add_struct(
            "All",
            vec![
                (1, "b", Dt::Bool),
                (2, "u", Dt::U16),
                (3, "i", Dt::I32),
                (4, "f", Dt::F64),
                (5, "c", Dt::named("Color")),
            ],
        )
        .set_default("All", 1, Value::Bool(true))
        .set_default("All", 2, Value::U16(300))
        .set_default("All", 3, Value::I32(-5))
        .set_default("All", 4, Value::F64(1.5))
        .set_default("All", 5, Value::Enum(1))
        .build("All")
        .unwrap();
    let bytes = encode(&schema, &Value::Struct(vec![]), SchemaMode::Inline).unwrap();
    // All absent → all defaults surface in the dump (enum shows its name).
    assert_eq!(
        dump_json(&bytes).unwrap(),
        r#"{"b":true,"u":300,"i":-5,"f":1.5,"c":"Green"}"#
    );
    // And the schema survives a canonical round-trip unchanged.
    assert_eq!(
        verit::Schema::from_canonical(schema.canonical_bytes())
            .unwrap()
            .id(),
        schema.id()
    );
}

#[test]
fn a_default_whose_type_mismatches_is_rejected() {
    // A string default on a u32 field.
    assert!(SchemaBuilder::new()
        .add_struct("Rec", vec![(1, "n", Dt::U32)])
        .set_default("Rec", 1, Value::str("nope"))
        .build("Rec")
        .is_err());
    // A default on a non-scalar (list) field.
    assert!(SchemaBuilder::new()
        .add_struct("Rec", vec![(1, "xs", Dt::list(Dt::U8))])
        .set_default("Rec", 1, Value::U8(1))
        .build("Rec")
        .is_err());
}

#[test]
fn evolution_reader_default_fills_a_field_the_writer_never_had() {
    // Writer v0 has no `n` field at all; reader v1 adds it with a default.
    let v0 = SchemaBuilder::new()
        .add_struct("Rec", vec![(2, "name", Dt::Str)])
        .build("Rec")
        .unwrap();
    let bytes = encode(
        &v0,
        &Value::Struct(vec![(2, Value::str("legacy"))]),
        SchemaMode::HashOnly,
    )
    .unwrap();

    let v1 = rec_schema();
    let resolver = Resolver::new(&v0, &v1).unwrap();
    let msg = Message::parse(&bytes).unwrap();
    let root = msg.root(&resolver).unwrap();
    assert_eq!(root.get_u32(1).unwrap(), None, "absent in v0 data");
    assert!(
        matches!(root.get_or_default(1).unwrap(), Some(Ref::U32(7))),
        "reader's default fills the field the writer never wrote"
    );
}

#[test]
fn idl_parses_default_syntax() {
    let schema = verit::idl::parse("struct Rec { 1: n u32 = 7  2: name string } root Rec").unwrap();
    assert_eq!(schema.id(), rec_schema().id());

    // Signed and bool literals too.
    let s2 = verit::idl::parse("struct R { 1: k i32 = -5  2: on bool = true } root R").unwrap();
    let hand = SchemaBuilder::new()
        .add_struct("R", vec![(1, "k", Dt::I32), (2, "on", Dt::Bool)])
        .set_default("R", 1, Value::I32(-5))
        .set_default("R", 2, Value::Bool(true))
        .build("R")
        .unwrap();
    assert_eq!(s2.id(), hand.id());
}