chappi-core 0.1.0

Typed cognitive graph, patch, reentry, projection, and measurement engine for Chappi
Documentation
use std::fmt::Write as _;
use std::fs;
use std::path::PathBuf;

use chappi_core::{CognitiveState, Patch, apply_patch, snapshot_source, state_hash, validate};
use chappi_syntax::{canonical_hash, parse_document};

fn example_path(name: &str) -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("../..")
        .join("examples/v1")
        .join(name)
}

fn decode_state(source: &str) -> CognitiveState {
    let state =
        CognitiveState::try_from(parse_document(source).expect("example syntax must parse"))
            .expect("example snapshot must decode");
    assert!(
        validate(&state).is_empty(),
        "example snapshot must validate"
    );
    state
}

#[test]
fn canonical_example_hash_is_cross_platform_stable() {
    let source = fs::read_to_string(example_path("state.chappi")).expect("example must exist");
    let document = parse_document(&source).expect("example syntax must parse");
    let state = CognitiveState::try_from(document.clone()).expect("example snapshot must decode");
    assert!(
        validate(&state).is_empty(),
        "example snapshot must validate"
    );
    assert_eq!(snapshot_source(&state), source.as_bytes());

    let actual = hash_hex(canonical_hash(&document));
    assert_eq!(
        actual,
        "147d76efee99185273151735d59047e0af66cdd1aee0d4340ac70137c7037d04"
    );
}

#[test]
fn semantic_state_hash_matches_the_documented_patch_base() {
    let source = fs::read_to_string(example_path("state.chappi")).expect("example must exist");
    let state = decode_state(&source);

    assert_eq!(
        hash_hex(state_hash(&state)),
        "2f8e28da99eddbaae241e3ab7d2030cb8d655958b14e57eeba930f6723e2b7b7"
    );
}

#[test]
fn documented_patch_applies_to_the_canonical_example() {
    let state_source =
        fs::read_to_string(example_path("state.chappi")).expect("snapshot example must exist");
    let patch_source =
        fs::read_to_string(example_path("update.patch")).expect("patch example must exist");
    let state = decode_state(&state_source);
    let patch =
        Patch::try_from(parse_document(&patch_source).expect("example patch syntax must parse"))
            .expect("example patch must decode");

    let updated = apply_patch(&state, &patch).expect("documented patch must apply");

    assert_eq!(updated.nodes.len(), state.nodes.len() + 2);
    assert_eq!(updated.focus_streams[0].frames.len(), 2);
}

fn hash_hex(hash: [u8; 32]) -> String {
    hash.iter().fold(String::new(), |mut output, byte| {
        write!(&mut output, "{byte:02x}").expect("writing to String cannot fail");
        output
    })
}