use crate::preview::mermaid::flowchart::{Direction, Stroke};
use crate::preview::mermaid::mindmap::{Mindmap, NodeShape};
use crate::preview::mermaid::text_metrics;
use super::{
lay_out_spec, shapes, svg, Diagram, Glyph, GraphSpec, Label, RenderError, SpecEdge, SpecNode,
Theme, Tip,
};
pub fn render(code: &str, theme: &str) -> Result<String, RenderError> {
let map = crate::preview::mermaid::mindmap::parse(code)?;
let laid = lay_out(&map)?;
Ok(svg::emit(&laid, &Theme::named(theme)))
}
pub fn lay_out(map: &Mindmap) -> Result<Diagram, RenderError> {
if !text_metrics::fonts_available() {
return Err(RenderError::NoFonts);
}
if map.nodes.is_empty() {
return Err(RenderError::NothingToDraw);
}
lay_out_spec(&spec_of(map))
}
pub fn spec_of(map: &Mindmap) -> GraphSpec {
let ids: Vec<String> = (0..map.nodes.len()).map(|i| format!("n{i}")).collect();
let nodes: Vec<SpecNode> = map
.nodes
.iter()
.enumerate()
.map(|(i, node)| {
let label = Label::measure(&node.label);
let glyph = glyph_of(node.shape);
let size = shapes::size(glyph, super::Size::new(label.width, label.height));
SpecNode {
id: ids[i].clone(),
glyph,
label,
size,
panel: None,
}
})
.collect();
let edges: Vec<SpecEdge> = map
.nodes
.iter()
.enumerate()
.filter_map(|(i, node)| {
let parent = node.parent?;
Some(SpecEdge {
id: format!("e{i}"),
from: ids[parent].clone(),
to: ids[i].clone(),
label: None,
tip_start: Tip::None,
tip_end: Tip::None,
stroke: Stroke::Normal,
minlen: 1,
start_label: None,
end_label: None,
})
})
.collect();
GraphSpec {
direction: Direction::LeftToRight,
nodes,
edges,
blocks: Vec::new(),
}
}
fn glyph_of(shape: NodeShape) -> Glyph {
use crate::preview::mermaid::flowchart::Shape;
match shape {
NodeShape::NoBorder => Glyph::Underline,
NodeShape::RoundedRect => Glyph::Flow(Shape::RoundedRect),
NodeShape::Rect => Glyph::Flow(Shape::Rect),
NodeShape::Circle => Glyph::Flow(Shape::Circle),
NodeShape::Cloud => Glyph::Cloud,
NodeShape::Bang => Glyph::Bang,
NodeShape::Hexagon => Glyph::Flow(Shape::Hexagon),
}
}