Sign in

    #Flowchart values

    moonrockz/diagrams/flowchart provides immutable flowchart models without a syntax reader, renderer, host adapter or UI dependency. Mermaid flowcharts and both PlantUML activity dialects convert to this model. Callers can also construct it directly. Constructors copy caller-owned mutable collections.

    ///|
    test "construct an immutable flowchart with ordinary groups" {
    let nodes = [
    @flowchart.Node::new("Read", label="Read request", groups=["Work"]),
    @flowchart.Node::new("Save", label="Save", groups=["Work"]),
    ]
    let view = @flowchart.Flowchart::new(
    "request",
    nodes[:],
    [@flowchart.Connection::new("save", "Read", "Save")],
    direction=LR,
    groups=[@flowchart.Group::new("Work")],
    notes=[
    @flowchart.Note::new("Detail", "Source text", targets=["Read"], groups=[
    "Work",
    ]),
    ],
    )
    nodes[0] = @flowchart.Node::new("Changed")
    assert_eq(view.nodes[0].id, "Read")
    assert_eq(view.lanes.length(), 0)
    assert_eq(view.groups[0].id, "Work")
    assert_eq(view.connections[0].source, "Read")
    assert_eq(@flowchart.validate(view), [])
    }

    Nodes keep stable IDs, label kinds, vector shape identity, metadata, group paths, explicit lane ownership, typed roles, region membership and source bindings. Shape identity and control role are separate. A generic Mermaid diamond remains Generic; PlantUML conversion can supply Decision, Merge, Fork, Join, Start, ActivityFinal, FlowFinal, Connector or Action explicitly.

    Connections retain IDs, declared source/target, labels, line style, markers at both ends, length/direction hints, typed role and origin. Equal labels never merge separate occurrences. Global TB, BT, LR and RL affect placement without reversing stored endpoints. Source conversion accepts TD as TB.

    ///|
    test "control role and source identity are explicit values" {
    let origin = @flowchart.SourceBinding::new("caller", "actions/0")
    let action = @flowchart.Node::new("Review", role=Action, origin~)
    let view = @flowchart.Flowchart::new("review", [action], [])
    assert_eq(view.nodes[0].role, Action)
    assert_eq(view.nodes[0].origin.unwrap().path, "actions/0")
    assert_eq(view.nodes[0].origin.unwrap().span, None)
    assert_eq(@flowchart.validate(view), [])
    assert_true(!@flowchart.validate(view, work_limit=1).is_empty())
    }

    Groups retain their parent, label, optional local direction and expanded state. Ordinary subgraphs stay groups. Lanes represent explicit responsibility declarations and keep declared order. A PlantUML partition can span lane changes without changing a node's semantic group path. Notes retain node and connection attachments, text, placement and ownership. Note attachments are descriptive and never become control-flow route edges.

    Structured regions retain ordered branch and loop/parallel membership, conditions, entry/exit references and join specifications. These values describe source structure. The model never evaluates conditions, executes actions or implements token or synchronization semantics.

    validate uses explicit node, edge, depth and work limits. It reports duplicate identities, missing endpoints/note targets, ownership cycles, invalid group paths and inconsistent region references without repairing them. Source bindings use the shared half-open UTF-16 span contract when a source position exists. Construction, validation and transformations are deterministic on wasm, wasm-gc, js and native.

    Pass Diagram::Flowchart(view) to the shared SVG or animated renderer. Geometry, scene bindings, finite plans, structural routes and reachability are separate packages. See flowcharts and animation and run mise run test:flowchart-consumer for fresh model-only, native-reader and SVG/player consumers on all four targets.

    Branch

    pub struct Branch {
    id : String
    label : String
    members : ReadOnlyArray[String]
    condition : String?
    origin : SourceBinding?
    } derive(Eq,
    Debug
    )

    Branch::equal

    fn Branch::equal(Branch, Branch) -> Bool

    Branch::new

    fn Branch::new(id : String, label? : String, members? : ArrayView[String], condition? : String, origin? : SourceBinding) -> Branch

    Branch::not_equal

    fn Branch::not_equal(x : Branch, y : Branch) -> Bool

    Branch::to_repr

    Connection

    pub struct Connection {
    id : String
    source : String
    target : String
    label : String
    label_kind : LabelKind
    style : LineStyle
    source_marker : Marker
    target_marker : Marker
    min_length : Int
    direction : FlowDirection
    direction_hint : Direction?
    role : ConnectionRole
    origin : SourceBinding?
    } derive(Eq,
    Debug
    )

    Connection::equal

    fn Connection::equal(Connection, Connection) -> Bool

    Connection::new

    fn Connection::new(id : String, source : String, target : String, label? : String, label_kind? : LabelKind, style? : LineStyle, source_marker? : Marker, target_marker? : Marker, min_length? : Int, direction_hint? : Direction, role? : ConnectionRole, origin? : SourceBinding) -> Connection

    Connection::not_equal

    fn Connection::not_equal(x : Connection, y : Connection) -> Bool

    ConnectionRole

    pub(all) enum ConnectionRole {
    Flow
    Branch
    LoopBack
    LoopExit
    Parallel
    NoteAttachment
    Layout
    } derive(Eq,
    Debug
    )

    ConnectionRole::equal

    ConnectionRole::not_equal

    fn ConnectionRole::not_equal(x : ConnectionRole, y : ConnectionRole) -> Bool

    Direction

    pub(all) enum Direction {
    TB
    BT
    LR
    RL
    } derive(Eq,
    Debug
    )

    Direction::equal

    fn Direction::equal(Direction, Direction) -> Bool

    Direction::not_equal

    fn Direction::not_equal(x : Direction, y : Direction) -> Bool

    FlowDirection

    pub(all) enum FlowDirection {
    Forward
    Backward
    Both
    Undirected
    Invisible
    } derive(Eq,
    Debug
    )

    FlowDirection::equal

    FlowDirection::not_equal

    fn FlowDirection::not_equal(x : FlowDirection, y : FlowDirection) -> Bool

    Flowchart

    pub struct Flowchart {
    notes : ReadOnlyArray[Note]
    regions : ReadOnlyArray[Region]
    id : String
    direction : Direction
    lanes : ReadOnlyArray[Lane]
    groups : ReadOnlyArray[Group]
    nodes : ReadOnlyArray[Node]
    connections : ReadOnlyArray[Connection]
    title : String?
    accessibility_title : String?
    accessibility_description : String?
    } derive(Eq,
    Debug
    )

    Flowchart::equal

    fn Flowchart::equal(Flowchart, Flowchart) -> Bool

    Flowchart::new

    fn Flowchart::new(id : String, nodes : ArrayView[Node], connections : ArrayView[Connection], direction? : Direction, lanes? : ArrayView[Lane], groups? : ArrayView[Group], title? : String, accessibility_title? : String, accessibility_description? : String, notes? : ArrayView[Note], regions? : ArrayView[Region]) -> Flowchart

    Flowchart::not_equal

    fn Flowchart::not_equal(x : Flowchart, y : Flowchart) -> Bool

    Flowchart::with_nodes

    fn Flowchart::with_nodes(self : Flowchart, nodes : ArrayView[Node]) -> Flowchart

    Replace the ordered nodes, copying the caller's collection.

    Group

    pub struct Group {
    id : String
    label : String
    parent : String?
    lane : String?
    direction : Direction?
    expanded : Bool
    } derive(Eq,
    Debug
    )

    Group::equal

    fn Group::equal(Group, Group) -> Bool

    Group::new

    fn Group::new(id : String, label? : String, parent? : String, lane? : String, direction? : Direction, expanded? : Bool) -> Group

    Group::not_equal

    fn Group::not_equal(x : Group, y : Group) -> Bool

    Group::to_repr

    LabelKind

    pub(all) enum LabelKind {
    Text
    Markdown
    Html
    } derive(Eq,
    Debug
    )

    LabelKind::equal

    fn LabelKind::equal(LabelKind, LabelKind) -> Bool

    LabelKind::not_equal

    fn LabelKind::not_equal(x : LabelKind, y : LabelKind) -> Bool

    Lane

    pub struct Lane {
    id : String
    label : String
    direction : Direction?
    expanded : Bool
    } derive(Eq,
    Debug
    )

    Lane::equal

    fn Lane::equal(Lane, Lane) -> Bool

    Lane::new

    fn Lane::new(id : String, label? : String, direction? : Direction, expanded? : Bool) -> Lane

    Lane::not_equal

    fn Lane::not_equal(x : Lane, y : Lane) -> Bool

    Lane::to_repr

    LineStyle

    pub(all) enum LineStyle {
    Solid
    Dotted
    Thick
    Invisible
    } derive(Eq,
    Debug
    )

    LineStyle::equal

    fn LineStyle::equal(LineStyle, LineStyle) -> Bool

    LineStyle::not_equal

    fn LineStyle::not_equal(x : LineStyle, y : LineStyle) -> Bool

    Marker

    pub(all) enum Marker {
    None
    Arrow
    Circle
    Cross
    } derive(Eq,
    Debug
    )

    Marker::equal

    fn Marker::equal(Marker, Marker) -> Bool

    Marker::not_equal

    fn Marker::not_equal(x : Marker, y : Marker) -> Bool

    Marker::to_repr

    Node

    pub struct Node {
    id : String
    label : String
    label_kind : LabelKind
    shape : String
    metadata : ReadOnlyArray[(String, String)]
    lane : String?
    groups : ReadOnlyArray[String]
    properties : RenderProperties?
    role : Role
    regions : ReadOnlyArray[String]
    origin : SourceBinding?
    } derive(Eq,
    Debug
    )

    Node::equal

    fn Node::equal(Node, Node) -> Bool

    Node::new

    fn Node::new(id : String, label? : String, label_kind? : LabelKind, shape? : String, metadata? : ArrayView[(String, String)], lane? : String, groups? : ArrayView[String], properties? : RenderProperties, role? : Role, regions? : ArrayView[String], origin? : SourceBinding) -> Node

    Node::not_equal

    fn Node::not_equal(x : Node, y : Node) -> Bool

    Node::to_repr

    Node::with_label

    fn Node::with_label(self : Node, label : String) -> Node

    Replace a label while preserving identity, control role and source binding.

    Note

    pub struct Note {
    id : String
    text : String
    targets : ReadOnlyArray[String]
    connection_targets : ReadOnlyArray[String]
    placement : String
    groups : ReadOnlyArray[String]
    lane : String?
    origin : SourceBinding?
    } derive(Eq,
    Debug
    )

    Note::equal

    fn Note::equal(Note, Note) -> Bool

    Note::new

    fn Note::new(id : String, text : String, targets? : ArrayView[String], connection_targets? : ArrayView[String], placement? : String, groups? : ArrayView[String], lane? : String, origin? : SourceBinding) -> Note

    Note::not_equal

    fn Note::not_equal(x : Note, y : Note) -> Bool

    Note::to_repr

    Region

    pub struct Region {
    id : String
    kind : RegionKind
    parent : String?
    entry : String?
    exit : String?
    members : ReadOnlyArray[String]
    branches : ReadOnlyArray[Branch]
    condition : String?
    join_specification : String?
    origin : SourceBinding?
    } derive(Eq,
    Debug
    )

    Region::equal

    fn Region::equal(Region, Region) -> Bool

    Region::new

    fn Region::new(id : String, kind : RegionKind, parent? : String, entry? : String, exit? : String, members? : ArrayView[String], branches? : ArrayView[Branch], condition? : String, join_specification? : String, origin? : SourceBinding) -> Region

    Region::not_equal

    fn Region::not_equal(x : Region, y : Region) -> Bool

    Region::to_repr

    RegionKind

    pub(all) enum RegionKind {
    Conditional
    Loop
    Parallel
    Split
    } derive(Eq,
    Debug
    )

    RegionKind::equal

    fn RegionKind::equal(RegionKind, RegionKind) -> Bool

    RegionKind::not_equal

    fn RegionKind::not_equal(x : RegionKind, y : RegionKind) -> Bool

    RenderProperties

    pub struct RenderProperties {
    link : String?
    metadata : ReadOnlyArray[(String, String)]
    } derive(Eq,
    Debug
    )

    RenderProperties::equal

    RenderProperties::new

    fn RenderProperties::new(link? : String, metadata? : ArrayView[(String, String)]) -> RenderProperties

    RenderProperties::not_equal

    fn RenderProperties::not_equal(x : RenderProperties, y : RenderProperties) -> Bool

    Role

    pub(all) enum Role {
    Generic
    Action
    Decision
    Merge
    Fork
    Join
    Start
    ActivityFinal
    FlowFinal
    Connector
    } derive(Eq,
    Debug
    )

    Role::equal

    fn Role::equal(Role, Role) -> Bool

    Role::not_equal

    fn Role::not_equal(x : Role, y : Role) -> Bool

    Role::to_repr

    SourceBinding

    pub struct SourceBinding {
    format : String
    path : String
    span :
    Span
    ?
    } derive(Eq,
    Debug
    )

    Explicit source binding; positions use the library UTF-16 span contract.

    SourceBinding::equal

    SourceBinding::new

    fn SourceBinding::new(format : String, path : String, span? :
    Span
    ) -> SourceBinding

    SourceBinding::not_equal

    fn SourceBinding::not_equal(x : SourceBinding, y : SourceBinding) -> Bool

    built_in_shapes

    let built_in_shapes : ReadOnlyArray[String]

    canonical_shape

    fn canonical_shape(value : String) -> String

    diagnostic_codes

    let diagnostic_codes : ReadOnlyArray[String]

    direction

    fn direction(value : String) -> Direction?

    fresh_id

    fn fresh_id(stem : String, used : ArrayView[String], work_limit? : Int) -> Result[String,
    Diagnostic
    ]

    Allocate a deterministic identity without colliding with authored identities.

    validate

    fn validate(model : Flowchart, nodes_limit? : Int, edges_limit? : Int, depth_limit? : Int, work_limit? : Int) -> ReadOnlyArray[
    Diagnostic
    ]

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io