ariel-rs 0.2.0

A faithful Rust port of Mermaid JS — headless SVG diagram rendering without a browser
Documentation
/// Render a Mermaid-compatible error SVG — pixel-perfect copy of Mermaid JS's errorRenderer.ts.
///
/// - `version`: shown as "mermaid version {version}" in the second text line
/// - `_diagram_type`, `_message`: reserved for future structured error reporting
pub fn render_error_svg(_diagram_type: &str, _message: &str) -> String {
    render_error_svg_versioned("ariel-rs")
}

pub(crate) fn render_error_svg_versioned(version: &str) -> String {
    // All 6 icon paths copied verbatim from Mermaid's errorRenderer.ts.
    // Colors and strokes are applied via CSS classes (error-icon / error-text),
    // exactly as Mermaid does — no inline fill/stroke attributes on the paths.
    let esc_version = version
        .replace('&', "&")
        .replace('<', "&lt;")
        .replace('>', "&gt;");

    format!(
        concat!(
            r#"<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="512" viewBox="0 0 2412 512">"#,
            r#"<style>"#,
            r#".error-icon{{fill:#552222;}}"#,
            r#".error-text{{fill:#552222;stroke:#552222;}}"#,
            r#"</style>"#,
            r#"<g>"#,
            // Path 1 — main gear/wrench shape
            r#"<path class="error-icon" d="m411.313,123.313c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32-9.375,9.375-20.688-20.688c-12.484-12.5-32.766-12.5-45.25,0l-16,16c-1.261,1.261-2.304,2.648-3.31,4.051-21.739-8.561-45.324-13.426-70.065-13.426-105.867,0-192,86.133-192,192s86.133,192 192,192 192-86.133 192-192c0-24.741-4.864-48.327-13.426-70.065 1.402-1.007 2.79-2.049 4.051-3.31l16-16c12.5-12.492 12.5-32.758 0-45.25l-20.688-20.688 9.375-9.375 32.001-31.999zm-219.313,100.687c-52.938,0-96,43.063-96,96 0,8.836-7.164,16-16,16s-16-7.164-16-16c0-70.578 57.422-128 128-128 8.836,0 16,7.164 16,16s-7.164,16-16,16z"/>"#,
            // Path 2
            r#"<path class="error-icon" d="m459.02,148.98c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l16,16c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16.001-16z"/>"#,
            // Path 3
            r#"<path class="error-icon" d="m340.395,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688 6.25-6.25 6.25-16.375 0-22.625l-16-16c-6.25-6.25-16.375-6.25-22.625,0s-6.25,16.375 0,22.625l15.999,16z"/>"#,
            // Path 4
            r#"<path class="error-icon" d="m400,64c8.844,0 16-7.164 16-16v-32c0-8.836-7.156-16-16-16-8.844,0-16,7.164-16,16v32c0,8.836 7.156,16 16,16z"/>"#,
            // Path 5
            r#"<path class="error-icon" d="m496,96.586h-32c-8.844,0-16,7.164-16,16 0,8.836 7.156,16 16,16h32c8.844,0 16-7.164 16-16 0-8.836-7.156-16-16-16z"/>"#,
            // Path 6
            r#"<path class="error-icon" d="m436.98,75.605c3.125,3.125 7.219,4.688 11.313,4.688 4.094,0 8.188-1.563 11.313-4.688l32-32c6.25-6.25 6.25-16.375 0-22.625s-16.375-6.25-22.625,0l-32,32c-6.251,6.25-6.251,16.375-0.001,22.625z"/>"#,
            // Text 1 — exact coordinates from JS source
            r#"<text class="error-text" x="1440" y="250" font-size="150px" style="text-anchor:middle;">Syntax error in text</text>"#,
            // Text 2 — exact coordinates from JS source (x=1250, not 1440)
            r#"<text class="error-text" x="1250" y="400" font-size="100px" style="text-anchor:middle;">mermaid version {version}</text>"#,
            r#"</g>"#,
            r#"</svg>"#
        ),
        version = esc_version
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn error_svg_contains_svg_tag() {
        let svg = render_error_svg("flowchart", "something went wrong");
        assert!(svg.contains("<svg"));
    }

    #[test]
    fn error_svg_contains_error_icon_class() {
        let svg = render_error_svg("pie", "parse failed");
        assert!(svg.contains("error-icon"));
    }

    #[test]
    fn error_svg_contains_syntax_error_text() {
        let svg = render_error_svg("sequence", "bad token");
        assert!(svg.contains("Syntax error in text"));
    }

    #[test]
    fn error_svg_versioned_contains_version() {
        let svg = render_error_svg_versioned("1.2.3");
        assert!(svg.contains("1.2.3"));
    }

    #[test]
    fn error_svg_versioned_escapes_special_chars() {
        let svg = render_error_svg_versioned("a&b<c>d");
        assert!(svg.contains("a&amp;b&lt;c&gt;d"));
    }
}