Skip to main content

merman_core/diagram/
mod.rs

1use crate::{Error, ParseMetadata, Result};
2use serde_json::Value;
3
4pub type DiagramSemanticParser = fn(code: &str, meta: &ParseMetadata) -> Result<Value>;
5pub type RenderSemanticParser = fn(code: &str, meta: &ParseMetadata) -> Result<RenderSemanticModel>;
6
7#[derive(Debug, Clone, Default)]
8pub struct DiagramRegistry {
9    parsers: std::collections::HashMap<&'static str, DiagramSemanticParser>,
10}
11
12impl DiagramRegistry {
13    pub fn new() -> Self {
14        Self::default()
15    }
16
17    pub fn insert(&mut self, diagram_type: &'static str, parser: DiagramSemanticParser) {
18        self.parsers.insert(diagram_type, parser);
19    }
20
21    pub fn get(&self, diagram_type: &str) -> Option<DiagramSemanticParser> {
22        self.parsers.get(diagram_type).copied()
23    }
24
25    pub fn default_mermaid_11_12_2() -> Self {
26        let mut reg = Self::new();
27
28        reg.insert("error", crate::diagrams::error_diagram::parse_error);
29
30        reg.insert("flowchart-v2", crate::diagrams::flowchart::parse_flowchart);
31        reg.insert("flowchart", crate::diagrams::flowchart::parse_flowchart);
32        reg.insert("flowchart-elk", crate::diagrams::flowchart::parse_flowchart);
33
34        reg.insert("info", crate::diagrams::info::parse_info);
35        reg.insert("pie", crate::diagrams::pie::parse_pie);
36        reg.insert("c4", crate::diagrams::c4::parse_c4);
37        reg.insert(
38            "requirement",
39            crate::diagrams::requirement::parse_requirement,
40        );
41        reg.insert("sequence", crate::diagrams::sequence::parse_sequence);
42        reg.insert("zenuml", crate::diagrams::zenuml::parse_zenuml);
43
44        reg.insert("classDiagram", crate::diagrams::class::parse_class);
45        reg.insert("class", crate::diagrams::class::parse_class);
46
47        reg.insert("er", crate::diagrams::er::parse_er);
48        reg.insert("erDiagram", crate::diagrams::er::parse_er);
49
50        reg.insert("stateDiagram", crate::diagrams::state::parse_state);
51        reg.insert("state", crate::diagrams::state::parse_state);
52
53        reg.insert("mindmap", crate::diagrams::mindmap::parse_mindmap);
54        reg.insert("gantt", crate::diagrams::gantt::parse_gantt);
55        reg.insert("timeline", crate::diagrams::timeline::parse_timeline);
56        reg.insert("journey", crate::diagrams::journey::parse_journey);
57        reg.insert("kanban", crate::diagrams::kanban::parse_kanban);
58        reg.insert(
59            "architecture",
60            crate::diagrams::architecture::parse_architecture,
61        );
62        reg.insert("block", crate::diagrams::block::parse_block);
63        reg.insert("gitGraph", crate::diagrams::git_graph::parse_git_graph);
64        reg.insert(
65            "quadrantChart",
66            crate::diagrams::quadrant_chart::parse_quadrant_chart,
67        );
68        reg.insert("packet", crate::diagrams::packet::parse_packet);
69        reg.insert("radar", crate::diagrams::radar::parse_radar);
70        reg.insert("treemap", crate::diagrams::treemap::parse_treemap);
71        reg.insert("sankey", crate::diagrams::sankey::parse_sankey);
72        reg.insert("xychart", crate::diagrams::xychart::parse_xychart);
73
74        reg
75    }
76}
77
78#[derive(Debug, Clone)]
79pub struct ParsedDiagram {
80    pub meta: ParseMetadata,
81    pub model: Value,
82}
83
84#[derive(Debug, Clone)]
85pub enum RenderSemanticModel {
86    Json(Value),
87    Mindmap(crate::diagrams::mindmap::MindmapDiagramRenderModel),
88    State(crate::diagrams::state::StateDiagramRenderModel),
89    Sequence(crate::diagrams::sequence::SequenceDiagramRenderModel),
90    Flowchart(crate::diagrams::flowchart::FlowchartV2Model),
91    Architecture(crate::diagrams::architecture::ArchitectureDiagramRenderModel),
92    Class(crate::models::class_diagram::ClassDiagram),
93    C4(crate::diagrams::c4::C4DiagramRenderModel),
94    Kanban(crate::diagrams::kanban::KanbanDiagramRenderModel),
95    Gantt(crate::diagrams::gantt::GanttDiagramRenderModel),
96    Pie(crate::diagrams::pie::PieDiagramRenderModel),
97    Packet(crate::diagrams::packet::PacketDiagramRenderModel),
98    Timeline(crate::diagrams::timeline::TimelineDiagramRenderModel),
99    Journey(crate::diagrams::journey::JourneyDiagramRenderModel),
100    Requirement(crate::diagrams::requirement::RequirementDiagramRenderModel),
101    Sankey(crate::diagrams::sankey::SankeyDiagramRenderModel),
102    Radar(crate::diagrams::radar::RadarDiagramRenderModel),
103    Info(crate::diagrams::info::InfoDiagramRenderModel),
104    Treemap(crate::diagrams::treemap::TreemapDiagramRenderModel),
105    Block(crate::diagrams::block::BlockDiagramRenderModel),
106    Er(crate::diagrams::er::ErDiagramRenderModel),
107    QuadrantChart(crate::diagrams::quadrant_chart::QuadrantChartRenderModel),
108    XyChart(crate::diagrams::xychart::XyChartDiagramRenderModel),
109    GitGraph(crate::diagrams::git_graph::GitGraphRenderModel),
110}
111
112impl RenderSemanticModel {
113    pub fn kind(&self) -> &'static str {
114        match self {
115            Self::Json(_) => "json",
116            Self::Mindmap(_) => "mindmap",
117            Self::State(_) => "state",
118            Self::Sequence(_) => "sequence",
119            Self::Flowchart(_) => "flowchart",
120            Self::Architecture(_) => "architecture",
121            Self::Class(_) => "class",
122            Self::C4(_) => "c4",
123            Self::Kanban(_) => "kanban",
124            Self::Gantt(_) => "gantt",
125            Self::Pie(_) => "pie",
126            Self::Packet(_) => "packet",
127            Self::Timeline(_) => "timeline",
128            Self::Journey(_) => "journey",
129            Self::Requirement(_) => "requirement",
130            Self::Sankey(_) => "sankey",
131            Self::Radar(_) => "radar",
132            Self::Info(_) => "info",
133            Self::Treemap(_) => "treemap",
134            Self::Block(_) => "block",
135            Self::Er(_) => "er",
136            Self::QuadrantChart(_) => "quadrantChart",
137            Self::XyChart(_) => "xychart",
138            Self::GitGraph(_) => "gitGraph",
139        }
140    }
141
142    pub fn supports_diagram_type(&self, diagram_type: &str) -> bool {
143        match self {
144            Self::Json(_) => true,
145            Self::Mindmap(_) => diagram_type == "mindmap",
146            Self::State(_) => matches!(diagram_type, "stateDiagram" | "state"),
147            Self::Sequence(_) => matches!(diagram_type, "sequence" | "zenuml"),
148            Self::Flowchart(_) => {
149                matches!(diagram_type, "flowchart-v2" | "flowchart" | "flowchart-elk")
150            }
151            Self::Architecture(_) => diagram_type == "architecture",
152            Self::Class(_) => matches!(diagram_type, "classDiagram" | "class"),
153            Self::C4(_) => diagram_type == "c4",
154            Self::Kanban(_) => diagram_type == "kanban",
155            Self::Gantt(_) => diagram_type == "gantt",
156            Self::Pie(_) => diagram_type == "pie",
157            Self::Packet(_) => diagram_type == "packet",
158            Self::Timeline(_) => diagram_type == "timeline",
159            Self::Journey(_) => diagram_type == "journey",
160            Self::Requirement(_) => diagram_type == "requirement",
161            Self::Sankey(_) => diagram_type == "sankey",
162            Self::Radar(_) => diagram_type == "radar",
163            Self::Info(_) => diagram_type == "info",
164            Self::Treemap(_) => diagram_type == "treemap",
165            Self::Block(_) => diagram_type == "block",
166            Self::Er(_) => matches!(diagram_type, "er" | "erDiagram"),
167            Self::QuadrantChart(_) => diagram_type == "quadrantChart",
168            Self::XyChart(_) => diagram_type == "xychart",
169            Self::GitGraph(_) => diagram_type == "gitGraph",
170        }
171    }
172}
173
174#[derive(Debug, Clone, Default)]
175pub struct RenderDiagramRegistry {
176    parsers: std::collections::HashMap<&'static str, RenderSemanticParser>,
177}
178
179impl RenderDiagramRegistry {
180    pub fn new() -> Self {
181        Self::default()
182    }
183
184    pub fn insert(&mut self, diagram_type: &'static str, parser: RenderSemanticParser) {
185        self.parsers.insert(diagram_type, parser);
186    }
187
188    pub fn get(&self, diagram_type: &str) -> Option<RenderSemanticParser> {
189        self.parsers.get(diagram_type).copied()
190    }
191
192    pub fn default_mermaid_11_12_2() -> Self {
193        let mut reg = Self::new();
194
195        reg.insert("mindmap", |code, meta| {
196            crate::diagrams::mindmap::parse_mindmap_model_for_render(code, meta)
197                .map(RenderSemanticModel::Mindmap)
198        });
199        reg.insert("stateDiagram", |code, meta| {
200            crate::diagrams::state::parse_state_model_for_render(code, meta)
201                .map(RenderSemanticModel::State)
202        });
203        reg.insert("state", |code, meta| {
204            crate::diagrams::state::parse_state_model_for_render(code, meta)
205                .map(RenderSemanticModel::State)
206        });
207        reg.insert("zenuml", |code, meta| {
208            crate::diagrams::zenuml::parse_zenuml_model_for_render(code, meta)
209                .map(RenderSemanticModel::Sequence)
210        });
211        reg.insert("sequence", |code, meta| {
212            crate::diagrams::sequence::parse_sequence_model_for_render(code, meta)
213                .map(RenderSemanticModel::Sequence)
214        });
215        reg.insert("flowchart-v2", |code, meta| {
216            crate::diagrams::flowchart::parse_flowchart_model_for_render(code, meta)
217                .map(RenderSemanticModel::Flowchart)
218        });
219        reg.insert("flowchart", |code, meta| {
220            crate::diagrams::flowchart::parse_flowchart_model_for_render(code, meta)
221                .map(RenderSemanticModel::Flowchart)
222        });
223        reg.insert("flowchart-elk", |code, meta| {
224            crate::diagrams::flowchart::parse_flowchart_model_for_render(code, meta)
225                .map(RenderSemanticModel::Flowchart)
226        });
227        reg.insert("classDiagram", |code, meta| {
228            crate::diagrams::class::parse_class_typed(code, meta).map(RenderSemanticModel::Class)
229        });
230        reg.insert("class", |code, meta| {
231            crate::diagrams::class::parse_class_typed(code, meta).map(RenderSemanticModel::Class)
232        });
233        reg.insert("c4", |code, meta| {
234            crate::diagrams::c4::parse_c4_model_for_render(code, meta).map(RenderSemanticModel::C4)
235        });
236        reg.insert("architecture", |code, meta| {
237            crate::diagrams::architecture::parse_architecture_model_for_render(code, meta)
238                .map(RenderSemanticModel::Architecture)
239        });
240        reg.insert("kanban", |code, meta| {
241            crate::diagrams::kanban::parse_kanban_model_for_render(code, meta)
242                .map(RenderSemanticModel::Kanban)
243        });
244        reg.insert("gantt", |code, meta| {
245            crate::diagrams::gantt::parse_gantt_model_for_render(code, meta)
246                .map(RenderSemanticModel::Gantt)
247        });
248        reg.insert("pie", |code, meta| {
249            crate::diagrams::pie::parse_pie_model_for_render(code, meta)
250                .map(RenderSemanticModel::Pie)
251        });
252        reg.insert("packet", |code, meta| {
253            crate::diagrams::packet::parse_packet_model_for_render(code, meta)
254                .map(RenderSemanticModel::Packet)
255        });
256        reg.insert("timeline", |code, meta| {
257            crate::diagrams::timeline::parse_timeline_model_for_render(code, meta)
258                .map(RenderSemanticModel::Timeline)
259        });
260        reg.insert("journey", |code, meta| {
261            crate::diagrams::journey::parse_journey_model_for_render(code, meta)
262                .map(RenderSemanticModel::Journey)
263        });
264        reg.insert("requirement", |code, meta| {
265            crate::diagrams::requirement::parse_requirement_model_for_render(code, meta)
266                .map(RenderSemanticModel::Requirement)
267        });
268        reg.insert("sankey", |code, meta| {
269            crate::diagrams::sankey::parse_sankey_model_for_render(code, meta)
270                .map(RenderSemanticModel::Sankey)
271        });
272        reg.insert("radar", |code, meta| {
273            crate::diagrams::radar::parse_radar_model_for_render(code, meta)
274                .map(RenderSemanticModel::Radar)
275        });
276        reg.insert("info", |code, meta| {
277            crate::diagrams::info::parse_info_model_for_render(code, meta)
278                .map(RenderSemanticModel::Info)
279        });
280        reg.insert("treemap", |code, meta| {
281            crate::diagrams::treemap::parse_treemap_model_for_render(code, meta)
282                .map(RenderSemanticModel::Treemap)
283        });
284        reg.insert("block", |code, meta| {
285            crate::diagrams::block::parse_block_model_for_render(code, meta)
286                .map(RenderSemanticModel::Block)
287        });
288        reg.insert("er", |code, meta| {
289            crate::diagrams::er::parse_er_model_for_render(code, meta).map(RenderSemanticModel::Er)
290        });
291        reg.insert("erDiagram", |code, meta| {
292            crate::diagrams::er::parse_er_model_for_render(code, meta).map(RenderSemanticModel::Er)
293        });
294        reg.insert("quadrantChart", |code, meta| {
295            crate::diagrams::quadrant_chart::parse_quadrant_chart_model_for_render(code, meta)
296                .map(RenderSemanticModel::QuadrantChart)
297        });
298        reg.insert("xychart", |code, meta| {
299            crate::diagrams::xychart::parse_xychart_model_for_render(code, meta)
300                .map(RenderSemanticModel::XyChart)
301        });
302        reg.insert("gitGraph", |code, meta| {
303            crate::diagrams::git_graph::parse_git_graph_model_for_render(code, meta)
304                .map(RenderSemanticModel::GitGraph)
305        });
306
307        reg
308    }
309}
310
311#[derive(Debug, Clone)]
312pub struct ParsedDiagramRender {
313    pub meta: ParseMetadata,
314    pub model: RenderSemanticModel,
315}
316
317pub fn parse_or_unsupported(
318    registry: &DiagramRegistry,
319    diagram_type: &str,
320    code: &str,
321    meta: &ParseMetadata,
322) -> Result<Value> {
323    let Some(parser) = registry.get(diagram_type) else {
324        return Err(Error::UnsupportedDiagram {
325            diagram_type: diagram_type.to_string(),
326        });
327    };
328    parser(code, meta)
329}