libvctrl_core 3.2.0

Reference implementations of the libvctrl contracts (in-memory store, SHA-512 hasher, binary codec)
Documentation
use std::io::Cursor;

use libvctrl_sha512 as _;
use proptest as _;

use libvctrl_core::codec::{BinaryDecoder, BinaryEncoder};
use libvctrl_handler::{
    Blob, Commit, CommitMeta, Decoder, Encoder, EntryKind, Tag, Tree, TreeEntry, UserID, VctrlError,
};

pub mod common;

fn make_user(name: &str, email: &str) -> Result<UserID, VctrlError> {
    UserID::new(name.to_string(), email.to_string())
}

fn make_meta(ts: i64, tz: i16) -> Result<CommitMeta, VctrlError> {
    CommitMeta::new(ts, tz, None)
}

#[test]
fn blob_roundtrip_through_public_api() -> Result<(), VctrlError> {
    let payload = vec![9_u8, 8, 7, 6];
    let blob = Blob::new(payload.clone())?;

    let mut buf = Vec::new();
    BinaryEncoder.encode_blob(&blob, &mut buf)?;

    let decoded = BinaryDecoder.decode_blob(Cursor::new(buf))?;
    assert_eq!(decoded.data(), payload.as_slice());

    Ok(())
}

#[test]
fn tree_roundtrip_through_public_api() -> Result<(), VctrlError> {
    let hash = common::make_hash(0x44)?;
    let entry = TreeEntry::new("file.txt".to_string(), EntryKind::Executable, hash)?;
    let tree = Tree::new(vec![entry])?;

    let mut buf = Vec::new();
    BinaryEncoder.encode_tree(&tree, &mut buf)?;

    let decoded = BinaryDecoder.decode_tree(Cursor::new(buf))?;
    assert_eq!(decoded.entries().len(), 1);
    let first = decoded
        .entries()
        .first()
        .ok_or_else(|| VctrlError::Other("expected entry".into()))?;
    assert_eq!(first.name(), "file.txt");
    assert_eq!(first.kind(), EntryKind::Executable);
    assert_eq!(*first.hash(), hash);

    Ok(())
}

#[test]
fn commit_roundtrip_through_public_api() -> Result<(), VctrlError> {
    let tree = common::make_hash(0x55)?;
    let parent = common::make_hash(0x56)?;
    let author = make_user("Alice", "alice@example.com")?;
    let committer = make_user("Bob", "bob@example.com")?;
    let message = "integration commit".to_string();
    let meta = make_meta(1_600_000_000, 0)?;

    let commit = Commit::with_meta(tree, vec![parent], author, committer, message.clone(), meta)?;

    let mut buf = Vec::new();
    BinaryEncoder.encode_commit(&commit, &mut buf)?;

    let decoded = BinaryDecoder.decode_commit(Cursor::new(buf))?;
    assert_eq!(decoded.tree(), &tree);
    assert_eq!(decoded.parents(), &[parent]);
    assert_eq!(decoded.author().name(), "Alice");
    assert_eq!(decoded.committer().email(), "bob@example.com");
    assert_eq!(decoded.message(), message);
    assert_eq!(decoded.meta().timestamp(), 1_600_000_000);
    assert_eq!(decoded.meta().timezone_offset(), 0);

    Ok(())
}

#[test]
fn tag_roundtrip_through_public_api() -> Result<(), VctrlError> {
    let target = common::make_hash(0x66)?;
    let tagger = make_user("Tagger", "tagger@example.com")?;
    let message = "v1.0".to_string();
    let meta = make_meta(1_600_000_000, 0)?;

    let tag = Tag::with_meta(
        "v1.0".to_string(),
        target,
        Some(tagger),
        message.clone(),
        meta,
    )?;

    let mut buf = Vec::new();
    BinaryEncoder.encode_tag(&tag, &mut buf)?;

    let decoded = BinaryDecoder.decode_tag(Cursor::new(buf))?;
    assert_eq!(decoded.name(), "v1.0");
    assert_eq!(decoded.target(), &target);
    let tagger = decoded
        .tagger()
        .ok_or_else(|| VctrlError::Other("expected tagger".into()))?;
    assert_eq!(tagger.name(), "Tagger");
    assert_eq!(decoded.message(), message);
    assert_eq!(decoded.meta().timestamp(), 1_600_000_000);
    assert_eq!(decoded.meta().timezone_offset(), 0);

    Ok(())
}