okf-core 0.2.5

A pure-Rust implementation of the Open Knowledge Format (OKF) v0.2 specification: parser, model, validator, provenance/trust/attestation families, link graph, and index/log tooling.
Documentation
use okf_core::markdown::{
    LinkRewriteAction, clean_destination, extract_headings, extract_title_suffix, heading_slug,
    matches_heading, parse_heading_line, rewrite_markdown_links, strip_title,
};

#[test]
fn test_heading_slug_derivation() {
    assert_eq!(heading_slug("Overview"), "overview");
    assert_eq!(heading_slug("## Deep Section"), "deep-section");
    assert_eq!(
        heading_slug("### Cost & Revenue Analysis (2026)"),
        "cost-revenue-analysis-2026"
    );
    assert_eq!(heading_slug("trailing-dash---"), "trailing-dash");
    assert_eq!(
        heading_slug("multiple   spaces___and---dashes"),
        "multiple-spaces-and-dashes"
    );
}

#[test]
fn test_parse_heading_line() {
    assert_eq!(parse_heading_line("# Level One"), Some((1, "Level One")));
    assert_eq!(parse_heading_line("## Level Two"), Some((2, "Level Two")));
    assert_eq!(
        parse_heading_line("###### Level Six"),
        Some((6, "Level Six"))
    );
    assert_eq!(parse_heading_line("####### Too Deep"), None);
    assert_eq!(parse_heading_line("#NoSpace"), None);
    assert_eq!(parse_heading_line("Not a heading"), None);
}

#[test]
fn test_extract_headings_skips_code_blocks() {
    let body = "\
# Main Title

```
# Code Block Heading
```

## Section 1

~~~python
### Python Heading
~~~

### Section 1.1
";
    let headings = extract_headings(body);
    assert_eq!(headings.len(), 3);
    assert_eq!(headings[0].level, 1);
    assert_eq!(headings[0].text, "Main Title");
    assert_eq!(headings[0].slug(), "main-title");

    assert_eq!(headings[1].level, 2);
    assert_eq!(headings[1].text, "Section 1");
    assert_eq!(headings[1].slug(), "section-1");

    assert_eq!(headings[2].level, 3);
    assert_eq!(headings[2].text, "Section 1.1");
    assert_eq!(headings[2].slug(), "section-11");
}

#[test]
fn test_matches_heading() {
    assert!(matches_heading("## Pricing Model", "Pricing Model"));
    assert!(matches_heading("pricing_model", "pricing-model"));
    assert!(matches_heading("Pricing-Model", "pricing model"));
    assert!(matches_heading("### Advanced Setup", "advanced-setup"));
    assert!(!matches_heading("Setup", "Different"));
}

#[test]
fn test_clean_destination_and_title_stripping() {
    assert_eq!(
        clean_destination("<path with spaces/file.md>"),
        "path with spaces/file.md"
    );
    assert_eq!(clean_destination("guide.md \"Guide Title\""), "guide.md");
    assert_eq!(clean_destination("guide.md 'Guide Title'"), "guide.md");
    assert_eq!(strip_title("guide.md \"Guide Title\""), "guide.md");
    assert_eq!(
        extract_title_suffix("guide.md \"Guide Title\""),
        " \"Guide Title\""
    );
}

#[test]
fn test_rewrite_markdown_links() {
    let body = "\
Check [Token Guide](auth/token.md \"Token\") and [Invoice](../billing/invoice.md).
Do not change `[Code](stay.md)` or:

```
[Inside](stay.md)
```
";
    let (rewritten, count) = rewrite_markdown_links(body, |link, _| {
        if link.target.contains("auth/token.md") {
            LinkRewriteAction::Rewrite("security/token.md".to_string())
        } else if link.target.contains("invoice.md") {
            LinkRewriteAction::Unlink
        } else {
            LinkRewriteAction::Keep
        }
    });

    assert_eq!(count, 2);
    assert!(rewritten.contains("[Token Guide](security/token.md \"Token\")"));
    assert!(rewritten.contains("and Invoice."));
    assert!(rewritten.contains("`[Code](stay.md)`"));
    assert!(rewritten.contains("[Inside](stay.md)"));
}