ytsaurus-skiff 0.2.5

Schema and streaming codec for the YTsaurus Skiff format (work in progress)
Documentation
use std::collections::BTreeMap;

use ytsaurus_skiff::{Format, Schema, SchemaError, SchemaRef, WireType};
use ytsaurus_yson::{YsonFormat, from_slice, to_string};

fn row_schema() -> Schema {
    Schema::tuple([
        Schema::named("found", WireType::Uint64),
        Schema::named("rcl", WireType::String32),
    ])
}

#[test]
fn renders_the_format_shape_ytsaurus_and_go_expect() {
    let format = Format::new(vec![SchemaRef::Inline(row_schema())]).unwrap();
    let rendered = to_string(&format.to_yson(), YsonFormat::Text).unwrap();

    assert_eq!(
        rendered,
        "<table_skiff_schemas=[{children=[{name=found;wire_type=uint64};{name=rcl;wire_type=string32}];wire_type=tuple}]>skiff"
    );
}

#[test]
fn round_trips_an_inline_schema_format() {
    let input = b"<table_skiff_schemas=[{wire_type=tuple;children=[{name=found;wire_type=uint64};{name=rcl;wire_type=string32}]}]>skiff";
    let value = from_slice(input, YsonFormat::Text).unwrap();

    let format = Format::from_yson(&value).unwrap();
    assert_eq!(format.table_schema(0).unwrap(), &row_schema());
}

#[test]
fn resolves_a_schema_registry_reference() {
    let registry = BTreeMap::from([("row".to_owned(), row_schema())]);
    let format = Format::from_parts(vec![SchemaRef::Registry("row".to_owned())], registry).unwrap();

    let parsed = Format::from_yson(&format.to_yson()).unwrap();
    assert_eq!(parsed.table_schema(0).unwrap(), &row_schema());
}

#[test]
fn rejects_an_unknown_registry_reference() {
    let err = Format::new(vec![SchemaRef::Registry("missing".to_owned())]).unwrap_err();
    assert_eq!(
        err,
        SchemaError::UnknownRegistryReference("missing".to_owned())
    );
}

#[test]
fn optional_column_is_the_go_sdk_variant8_shape() {
    let schema = Schema::named("found", WireType::Uint64).optional();
    assert_eq!(schema.wire_type, WireType::Variant8);
    assert_eq!(schema.name.as_deref(), Some("found"));
    assert_eq!(schema.children[0].wire_type, WireType::Nothing);
    assert_eq!(schema.children[1].wire_type, WireType::Uint64);
    schema.validate().unwrap();
}

#[test]
fn rejects_children_on_a_leaf() {
    let schema = Schema {
        wire_type: WireType::Uint64,
        name: None,
        children: vec![Schema::leaf(WireType::Nothing)],
    };

    assert_eq!(
        schema.validate(),
        Err(SchemaError::UnexpectedChildren {
            wire_type: WireType::Uint64,
            count: 1,
        })
    );
}

#[test]
fn table_schemas_are_named_tuples() {
    assert!(matches!(
        Format::new(vec![SchemaRef::Inline(Schema::leaf(WireType::Uint64))]),
        Err(SchemaError::TableSchemaRootMustBeTuple {
            found: WireType::Uint64,
        })
    ));

    assert!(matches!(
        Format::new(vec![SchemaRef::Inline(Schema::tuple([Schema::leaf(
            WireType::Uint64
        )]))]),
        Err(SchemaError::TableSchemaChildMissingName { index: 0 })
    ));
}

#[test]
fn supports_every_wire_type_named_by_go_sdk_v0_0_33() {
    let names = [
        (WireType::Nothing, "nothing"),
        (WireType::Boolean, "boolean"),
        (WireType::Int8, "int8"),
        (WireType::Int16, "int16"),
        (WireType::Int32, "int32"),
        (WireType::Int64, "int64"),
        (WireType::Int128, "int128"),
        (WireType::Int256, "int256"),
        (WireType::Uint8, "uint8"),
        (WireType::Uint16, "uint16"),
        (WireType::Uint32, "uint32"),
        (WireType::Uint64, "uint64"),
        (WireType::Double, "double"),
        (WireType::String32, "string32"),
        (WireType::Yson32, "yson32"),
        (WireType::Variant8, "variant8"),
        (WireType::Variant16, "variant16"),
        (WireType::RepeatedVariant8, "repeated_variant8"),
        (WireType::RepeatedVariant16, "repeated_variant16"),
        (WireType::Tuple, "tuple"),
    ];

    for (wire_type, expected) in names {
        assert_eq!(wire_type.as_str(), expected);
        assert_eq!(WireType::parse(expected), Some(wire_type));
    }
}