merman-core 0.5.1

Mermaid parser + semantic model (headless; parity-focused).
Documentation
use futures::executor::block_on;
use merman_core::{Engine, MAX_DIAGRAM_NESTING_DEPTH, ParseOptions};

fn parse_err(input: &str) -> String {
    let engine = Engine::new();
    block_on(engine.parse_diagram(input, ParseOptions::strict()))
        .expect_err("deeply nested diagram should return a parse error")
        .to_string()
}

fn assert_nesting_error(name: &str, input: String) {
    let err = parse_err(&input);
    assert!(
        err.contains("nesting depth exceeds maximum"),
        "{name} error should mention nesting depth, got: {err}"
    );
}

fn flowchart(depth: usize) -> String {
    let mut lines = vec!["flowchart TD".to_string()];
    for i in 0..depth {
        lines.push(format!("subgraph n{i}"));
    }
    lines.push("leaf[leaf]".to_string());
    for _ in 0..depth {
        lines.push("end".to_string());
    }
    lines.join("\n")
}

fn state(depth: usize) -> String {
    let mut lines = vec!["stateDiagram-v2".to_string()];
    for i in 0..depth {
        lines.push(format!("state s{i} {{"));
    }
    lines.push("leaf".to_string());
    for _ in 0..depth {
        lines.push("}".to_string());
    }
    lines.join("\n")
}

fn block(depth: usize) -> String {
    let mut lines = vec!["block-beta".to_string()];
    for i in 0..depth {
        lines.push(format!("{}block:b{i}", "  ".repeat(i)));
    }
    lines.push(format!("{}leaf", "  ".repeat(depth)));
    for i in (0..depth).rev() {
        lines.push(format!("{}end", "  ".repeat(i)));
    }
    lines.join("\n")
}

fn mindmap(depth: usize) -> String {
    let mut lines = vec!["mindmap".to_string(), "root".to_string()];
    for i in 0..depth {
        lines.push(format!("{}n{i}", "  ".repeat(i + 1)));
    }
    lines.join("\n")
}

fn treemap(depth: usize) -> String {
    let mut lines = vec!["treemap-beta".to_string()];
    for i in 0..depth {
        lines.push(format!("{}\"n{i}\"", "  ".repeat(i)));
    }
    lines.push(format!("{}\"leaf\": 1", "  ".repeat(depth)));
    lines.join("\n")
}

fn c4(depth: usize) -> String {
    let mut lines = vec!["C4Context".to_string()];
    for i in 0..depth {
        lines.push(format!("{}Boundary(b{i}, \"B{i}\") {{", "  ".repeat(i)));
    }
    lines.push(format!("{}System(s, \"S\")", "  ".repeat(depth)));
    for i in (0..depth).rev() {
        lines.push(format!("{}}}", "  ".repeat(i)));
    }
    lines.join("\n")
}

#[test]
fn deeply_nested_flowchart_returns_parse_error() {
    let depth = MAX_DIAGRAM_NESTING_DEPTH + 2;
    assert_nesting_error("flowchart", flowchart(depth));
}

#[test]
fn deeply_nested_state_returns_parse_error() {
    let depth = MAX_DIAGRAM_NESTING_DEPTH + 2;
    assert_nesting_error("state", state(depth));
}

#[test]
fn deeply_nested_block_returns_parse_error() {
    let depth = MAX_DIAGRAM_NESTING_DEPTH + 2;
    assert_nesting_error("block", block(depth));
}

#[test]
fn deeply_nested_mindmap_returns_parse_error() {
    let depth = MAX_DIAGRAM_NESTING_DEPTH + 2;
    assert_nesting_error("mindmap", mindmap(depth));
}

#[test]
fn deeply_nested_treemap_returns_parse_error() {
    let depth = MAX_DIAGRAM_NESTING_DEPTH + 2;
    assert_nesting_error("treemap", treemap(depth));
}

#[test]
fn deeply_nested_c4_returns_parse_error() {
    let depth = MAX_DIAGRAM_NESTING_DEPTH + 2;
    assert_nesting_error("c4", c4(depth));
}