substrate-data 0.2.1

One data model, text syntax, binary form, packed frames and schema system — for any program's files, saves and wire messages.
Documentation
//! Cases for the packed list tags and streams, written by the typed writers.

use substrate_data::binary::Writer;
use substrate_data::{Schema, Stream, Value, to_bytes};

use crate::case::Case;

/// Every case of this module.
pub(crate) fn cases() -> Vec<Case> {
    let mut all = packed_ints();
    all.extend(packed_floats());
    all.extend(streams());
    all
}

fn ints(items: &[i64]) -> Value {
    Value::List(items.iter().map(|&n| Value::Int(n)).collect())
}

fn floats(items: &[f64]) -> Value {
    Value::List(items.iter().map(|&f| Value::Float(f)).collect())
}

/// A case whose value is written as the binary `bytes`, and as text when `with_text`.
fn written(name: &str, description: &str, value: Value, bytes: Vec<u8>) -> Case {
    let text = substrate_data::text::to_string(&value);
    Case::new(name, description)
        .text(&text)
        .value(value)
        .binary(bytes)
}

fn packed_ints() -> Vec<Case> {
    let sets: [(&str, &[i64]); 4] = [
        ("empty", &[]),
        ("small", &[0, -1, 1, 63, -64]),
        ("varints", &[64, -65, 8192, 2_147_483_648]),
        ("extremes", &[i64::MIN, i64::MAX, 0]),
    ];
    sets.iter()
        .map(|&(name, items)| {
            let mut w = Writer::new();
            w.ints(items.iter().copied());
            written(
                &format!("ints_packed_{name}"),
                "A list of ints packed: tag 10, a count, then a zigzag varint per item with no tag each. Decodes to the same list as tag 6 would.",
                ints(items),
                w.finish(),
            )
        })
        .collect()
}

fn packed_floats() -> Vec<Case> {
    let mut single = Writer::new();
    single.float32(0.5);
    let mut narrow = Writer::new();
    narrow.floats(&[0.5, 1.5, -0.0]);
    let mut wide = Writer::new();
    wide.floats(&[0.1, 0.2]);
    let mut mixed = Writer::new();
    mixed.floats(&[0.5, 0.1]);
    vec![
        written(
            "float32_single",
            "A float that is an f32: tag 12, four bytes little-endian. Decodes to the f64 of the same value.",
            Value::Float(0.5),
            single.finish(),
        ),
        written(
            "floats_packed_narrow",
            "A writer picks tag 14 (f32 each) when every f64 survives the round trip through f32 bit for bit; -0.0 does.",
            floats(&[0.5, 1.5, -0.0]),
            narrow.finish(),
        ),
        written(
            "floats_packed_wide",
            "Tag 13 (f64 each) when any one does not: 0.1 is not an f32.",
            floats(&[0.1, 0.2]),
            wide.finish(),
        ),
        written(
            "floats_packed_mixed",
            "One value that is not an f32 widens the whole list, 0.5 included.",
            floats(&[0.5, 0.1]),
            mixed.finish(),
        ),
    ]
    .into_iter()
    .chain(more_floats())
    .collect()
}

fn more_floats() -> Vec<Case> {
    let mut empty = Writer::new();
    empty.floats(&[]);
    let mut f32s = Writer::new();
    f32s.floats32(&[0.5, f32::MAX]);
    let mut nan = Writer::new();
    nan.floats(&[f64::NAN, f64::INFINITY]);
    vec![
        written(
            "floats_packed_empty",
            "An empty list of floats picks the narrow tag 14.",
            Value::List(vec![]),
            empty.finish(),
        ),
        written(
            "floats32_packed",
            "A list of f32s: tag 14 always, 4 bytes each.",
            floats(&[0.5, f64::from(f32::MAX)]),
            f32s.finish(),
        ),
        nan_case(nan.finish()),
    ]
}

fn nan_case(bytes: Vec<u8>) -> Case {
    Case::new(
        "floats_packed_non_finite",
        "Binary only (text writes null for them): a quiet NaN and infinity survive f32 bit for bit, so the narrow tag 14 is used.",
    )
    .value(floats(&[f64::NAN, f64::INFINITY]))
    .binary(bytes)
}

/// A log: a name and a stream of entries.
#[derive(Schema, Debug)]
struct Log {
    name: String,
    entries: Stream<String>,
}

#[derive(Schema, Debug)]
struct Counter {
    hits: Stream<u32>,
}

fn streams() -> Vec<Case> {
    let entries =
        |items: &[&str]| -> Stream<String> { items.iter().map(|s| (*s).to_owned()).collect() };
    let empty = Log {
        name: "audit".into(),
        entries: entries(&[]),
    };
    let repeats = Log {
        name: "audit".into(),
        entries: entries(&["opened", "saved", "opened", "audit"]),
    };
    let counter = Counter {
        hits: (0..6u32).map(|i| i * 1000).collect(),
    };
    vec![
        schema_case(
            "stream_empty",
            "An empty stream: tag 11, a u32 byte length (here 1), then the count 0.",
            &empty,
        ),
        schema_case(
            "stream_own_table",
            "A stream has its own string table: `audit` is written again inside it although the document already has it, and `opened` is written once inside.",
            &repeats,
        ),
        schema_case(
            "stream_ints",
            "Stream items are values: six ints, each tag 3 and a zigzag varint.",
            &counter,
        ),
    ]
}

/// A case of a typed value: its text, its model value and its binary form.
fn schema_case<T: Schema>(name: &str, description: &str, x: &T) -> Case {
    let bytes = to_bytes(x);
    let value = substrate_data::binary::decode(&bytes).expect("the writer's own bytes");
    written(name, description, value, bytes)
}