Mermaid for diagrams.mbt: lossless CST, parser, AST, printer and conversion to diagram models
///|
test {
let source = "sequenceDiagram\n%% the client calls the API\nparticipant Client\nClient ->> API :GET /forecast\n"
let r = @mermaid.read(source)
assert_eq(r.tree.text(), source)
assert_eq(r.diagnostics.length(), 0)
let ast = r.ast.unwrap()
assert_eq(
@mermaid.print(ast),
"sequenceDiagram\n participant Client\n Client->>API: GET /forecast\n",
)
let model = @mermaid.to_sequence(ast).sequence
assert_eq(model.participants.length(), 2)
}///|
test {
let source = "sequenceDiagram\nALT Is sick\n B ->>- A : Not so good\nELSE is well\nNote Over A , B: fine\nEND\n"
let printed = @mermaid.print(@mermaid.read(source).ast.unwrap())
assert_eq(
printed, "sequenceDiagram\n alt Is sick\n B->>-A: Not so good\n else is well\n note over A,B: fine\n end\n",
)
assert_eq(@mermaid.read(printed).ast, @mermaid.read(source).ast)
}///|
test {
let r = @mermaid.read("sequenceDiagram\nA->>B: hi\nfrobnicate A\n")
let d = r.diagnostics[0]
assert_eq(d.code, "mermaid/unknown-statement")
assert_eq(d.span.unwrap().start.line, 3)
assert_eq(r.ast.unwrap().statements.length(), 1)
}| Code | Meaning |
|---|---|
| mermaid/missing-header | The source does not start with sequenceDiagram. There is no AST. |
| mermaid/unexpected-header | The source has a second sequenceDiagram. |
| mermaid/unknown-statement | The statement is not one that the grammar knows. |
| mermaid/missing-message-text | A message has no : and text. |
| mermaid/invalid-message | A message has no receiver. |
| mermaid/invalid-participant | A participant, actor, create, destroy, activate or deactivate statement has no valid name, or its @{...} configuration is not closed. |
| mermaid/invalid-note | A note has no placement, no participant or no text, or left of or right of has two participants. |
| mermaid/invalid-autonumber | autonumber has something other than a start, a step or off. |
| mermaid/invalid-link | A link, links, properties or details statement has no participant or no text. |
| mermaid/invalid-title | A title has no text. |
| mermaid/missing-end | A frame, such as loop or box, has no end. The span is the frame keyword. |
| mermaid/unexpected-end | An end has no open frame. |
| mermaid/misplaced-section | else is not in alt, and is not in par, or option is not in critical. |
| mermaid/invalid-in-box | A box has a statement that is not a participant declaration. |
| mermaid/unclosed-directive | A %%{ directive has no closing }%%. |
| mermaid/unclosed-description | An accDescr { has no closing }. |
///|
test {
assert_eq(@mermaid.diagnostic_codes.iter().to_array(), [
"mermaid/missing-header", "mermaid/unexpected-header", "mermaid/unknown-statement",
"mermaid/missing-message-text", "mermaid/invalid-message", "mermaid/invalid-participant",
"mermaid/invalid-note", "mermaid/invalid-autonumber", "mermaid/invalid-link",
"mermaid/invalid-title", "mermaid/missing-end", "mermaid/unexpected-end", "mermaid/misplaced-section",
"mermaid/invalid-in-box", "mermaid/unclosed-directive", "mermaid/unclosed-description",
])
}| Code | Meaning |
|---|---|
| mermaid/inactive-deactivation | The participant has no active activation. |
| mermaid/destroyed-participant | A statement uses a destroyed participant. |
| mermaid/conflicting-participant-kind | A declaration changes an already declared kind. |
| mermaid/invalid-participant-config | A configuration has invalid fields or an unknown kind. |
| mermaid/missing-destroying-message | A destroy statement has no associated next message involving its participant. |
| mermaid/invalid-links | A links value is not a JSON object. |
| mermaid/conflicting-box-group | A participant is declared in two different boxes. |
| mermaid/duplicate-creation | A create statement names an existing participant. |
///|
test {
let source = "sequenceDiagram\nparticipant W\nlink W: shape @ smithy:example.weather/Weather\nC->>W: example.weather/GetForecast\n"
let converted = @mermaid.to_sequence(@mermaid.read(source).ast.unwrap())
assert_eq(converted.diagnostics.length(), 0)
assert_eq(
converted.sequence.participant("W").unwrap().link.unwrap().target,
"example.weather#Weather",
)
}///|
test {
let r = @mermaid.render_svg("sequenceDiagram\nClient->>API: GET /forecast\n")
assert_eq(r.media_type, "image/svg+xml")
assert_true(
r.output.unwrap().has_prefix("<svg xmlns=\"http://www.w3.org/2000/svg\""),
)
}///|
test "read print and render a native two-lane swimlane" {
let source = "swimlane-beta LR\nsubgraph Customer\nA([Request])\nend\nsubgraph Support\nB{Review}\nend\nA handoff@--> B\n"
let result = @mermaid.read_diagram(source)
assert_eq(result.tree.text(), source)
assert_eq(result.diagnostics, [])
guard result.ast is Some(parsed) else { fail("missing AST") }
let printed = @mermaid.print_diagram(parsed)
assert_eq(@mermaid.read_diagram(printed.output.unwrap()).ast, Some(parsed))
guard parsed is Swimlane(ast) else { fail("wrong kind") }
let converted = @mermaid.to_swimlane(ast)
assert_eq(converted.diagnostics, [])
let view = converted.swimlane.unwrap()
assert_eq(view.lanes.length(), 2)
assert_eq(view.nodes[0].lane, Some("Customer"))
assert_eq(view.nodes[1].lane, Some("Support"))
assert_eq(view.connections[0].id, "handoff")
let rendered = @mermaid.render_svg(source)
assert_eq(rendered.media_type, "image/svg+xml")
assert_true(rendered.output is Some(_))
}///|
test "grouped expansion retains one native explicit ID and every occurrence" {
let read = @mermaid.read_diagram("swimlane-beta\nA & B e@--> C & D\n")
guard read.ast is Some(Swimlane(ast)) else { fail("missing AST") }
let view = @mermaid.to_swimlane(ast).swimlane.unwrap()
assert_eq(view.nodes.length(), 4)
assert_eq(view.connections.length(), 4)
assert_eq(view.connections.iter().filter(edge => edge.id == "e").count(), 1)
}| Code | Meaning |
|---|---|
| swimlane/source-limit | Source exceeds its configured UTF-16 limit. |
| swimlane/work-limit | Reading or conversion exhausts its work budget. |
| swimlane/depth-limit | Nesting exceeds the configured bound. |
| swimlane/unsupported-header | The swimlane reader receives another header. |
| swimlane/directive | A source directive is unclosed. |
| swimlane/unexpected-end | No group is open for end. |
| swimlane/syntax | A statement or its shape data is malformed. |
| swimlane/missing-end | A group has no closing end. |
| swimlane/node-limit | Normalized node count exceeds its bound. |
| swimlane/edge-limit | Expanded connections exceed their bound. |
| swimlane/direction | A declared direction has no supported model value. |
| swimlane/ownership-cycle | Normalized ownership is cyclic. |
| swimlane/ownership | Ownership references a missing group. |
| swimlane/invalid-ast | Typed values are inconsistent or cannot print as an equivalent native AST. |
| swimlane/presentation-metadata | A renderer-only feature remains unapplied source data. |
| swimlane/print-limit | Canonical printing exceeds a configured bound. |
| mermaid/unsupported-diagram | Kind dispatch receives an unsupported header. |
| mermaid/print-limit | Sequence output exceeds the new dispatch printer's bound. |
///|
test "documented native diagnostic inventories match exported code" {
assert_eq(@mermaid.swimlane_diagnostic_codes, [
"swimlane/source-limit", "swimlane/work-limit", "swimlane/depth-limit", "swimlane/unsupported-header",
"swimlane/directive", "swimlane/unexpected-end", "swimlane/syntax", "swimlane/missing-end",
])
assert_eq(@mermaid.swimlane_conversion_diagnostic_codes, [
"swimlane/work-limit", "swimlane/node-limit", "swimlane/edge-limit", "swimlane/depth-limit",
"swimlane/direction", "swimlane/ownership-cycle", "swimlane/ownership", "swimlane/invalid-ast",
"swimlane/presentation-metadata",
])
}pub struct DiagramPrintResult {
output : String?
diagnostics : ReadOnlyArray[Diagnostic]
} derive(Eq, Debug)pub struct DiagramReadResult {
tree : DiagramTree
ast : ParsedDiagram?
diagnostics : ReadOnlyArray[Diagnostic]
} derive(Eq, Debug)let conversion_diagnostic_codes : ReadOnlyArray[String]let diagnostic_codes : ReadOnlyArray[String]let swimlane_conversion_diagnostic_codes : ReadOnlyArray[String]let swimlane_diagnostic_codes : ReadOnlyArray[String]Install
Download zipMermaid for diagrams.mbt: lossless CST, parser, AST, printer and conversion to diagram models