weimo-wdf 0.1.1

Strict canonical rich-text document model with adapters for Markdown, Tiptap/ProseMirror JSON, Apple document JSON, and terminal output
Documentation
use serde_json::Value;
use std::{fs, path::PathBuf};
use weimo_wdf::{
    from_apple, from_markdown, from_tiptap, to_apple, to_markdown, to_tiptap,
    validate_and_canonicalize,
};

fn fixture(name: &str) -> Value {
    let path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
        .join("fixtures/core")
        .join(name)
        .join("expected.wdf.json");
    serde_json::from_str(&fs::read_to_string(path).expect("fixture")).expect("JSON fixture")
}

#[test]
fn markdown_and_tiptap_round_trip_shared_fixtures() {
    for name in [
        "paragraph-bold",
        "heading-levels",
        "blockquote",
        "bullet-list",
        "ordered-list-start-three",
        "mixed-document",
    ] {
        let expected = validate_and_canonicalize(&fixture(name)).expect("valid WDF");
        let markdown = to_markdown(&expected);
        assert_eq!(
            from_markdown(&markdown).expect("markdown parse"),
            expected,
            "markdown fixture {name}"
        );
        let tiptap = to_tiptap(&expected);
        assert_eq!(
            from_tiptap(&tiptap).expect("tiptap parse"),
            expected,
            "tiptap fixture {name}"
        );
    }
}

#[test]
fn apple_wire_round_trip_shared_fixture() {
    let expected = validate_and_canonicalize(&fixture("mixed-document")).expect("valid WDF");
    let apple = to_apple(&expected);
    assert_eq!(from_apple(&apple).expect("apple parse"), expected);
    assert_eq!(
        apple.get("type").and_then(Value::as_str),
        Some("apple.document")
    );
}

#[test]
fn tight_list_items_with_inline_marks_round_trip() {
    let markdown = "# Notes\n\n- **bold** item\n- a **b** c\n\n3. **b** start\n";
    let document = from_markdown(markdown).expect("tight list markdown");
    assert_eq!(to_markdown(&document), markdown);
    let diagnostics = from_markdown("- outer\n  - nested\n").expect_err("nested list must fail");
    assert!(
        diagnostics
            .iter()
            .any(|diagnostic| diagnostic.code.starts_with("markdown.unsupported"))
    );
}

#[test]
fn canonicalization_normalizes_newlines_and_ordered_start() {
    let input = serde_json::json!({"schema":"weimo.document","version":1,"root":{"type":"doc","content":[{"type":"ordered_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"a\r\nb"}]}]}]}]}});
    let output = validate_and_canonicalize(&input).expect("valid WDF");
    assert_eq!(to_markdown(&output), "1. a\nb\n");
    assert_eq!(output.root.content[0], serde_json::from_value(serde_json::json!({"type":"ordered_list","attrs":{"start":1},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"type":"text","text":"a\nb"}]}]}]})).expect("block"));
}

#[test]
fn invalid_unknown_mark_is_rejected() {
    let input = serde_json::json!({"schema":"weimo.document","version":1,"root":{"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"x","marks":[{"type":"italic"}]}]}]}});
    let diagnostics = validate_and_canonicalize(&input).expect_err("unknown mark must fail");
    assert!(
        diagnostics
            .iter()
            .any(|diagnostic| diagnostic.code == "wdf.unsupported_mark")
    );
}

#[test]
fn invalid_unknown_property_is_rejected() {
    let input = serde_json::json!({"schema":"weimo.document","version":1,"root":{"type":"doc","content":[{"type":"paragraph","content":[],"extra":true}]}});
    let diagnostics = validate_and_canonicalize(&input).expect_err("unknown property must fail");
    assert!(
        diagnostics
            .iter()
            .any(|diagnostic| diagnostic.code == "wdf.unknown_property")
    );
}

#[test]
fn invalid_tiptap_mark_is_rejected() {
    let input = serde_json::json!({"type":"doc","content":[{"type":"paragraph","content":[{"type":"text","text":"x","marks":[{"type":"italic"}]}]}]});
    let diagnostics = from_tiptap(&input).expect_err("unknown Tiptap mark must fail");
    assert!(
        diagnostics
            .iter()
            .any(|diagnostic| diagnostic.code == "prosemirror.unsupported_mark")
    );
}