hugr_core/hugr/views/
render.rs1use portgraph::render::{EdgeStyle, NodeStyle, PortStyle};
5use portgraph::{LinkView, NodeIndex, PortIndex, PortView};
6
7use crate::ops::{NamedOp, OpType};
8use crate::types::EdgeKind;
9use crate::HugrView;
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13#[non_exhaustive]
14pub struct RenderConfig {
15 pub node_indices: bool,
17 pub port_offsets_in_edges: bool,
19 pub type_labels_in_edges: bool,
21}
22
23impl Default for RenderConfig {
24 fn default() -> Self {
25 Self {
26 node_indices: true,
27 port_offsets_in_edges: true,
28 type_labels_in_edges: true,
29 }
30 }
31}
32
33pub(super) fn node_style<H: HugrView + ?Sized>(
35 h: &H,
36 config: RenderConfig,
37) -> Box<dyn FnMut(NodeIndex) -> NodeStyle + '_> {
38 fn node_name<H: HugrView + ?Sized>(h: &H, n: NodeIndex) -> String {
39 match h.get_optype(h.get_node(n)) {
40 OpType::FuncDecl(f) => format!("FuncDecl: \"{}\"", f.name),
41 OpType::FuncDefn(f) => format!("FuncDefn: \"{}\"", f.name),
42 op => op.name().to_string(),
43 }
44 }
45
46 if config.node_indices {
47 Box::new(move |n| {
48 NodeStyle::Box(format!(
49 "({ni}) {name}",
50 ni = n.index(),
51 name = node_name(h, n)
52 ))
53 })
54 } else {
55 Box::new(move |n| NodeStyle::Box(node_name(h, n)))
56 }
57}
58
59pub(super) fn port_style<H: HugrView + ?Sized>(
61 h: &H,
62 _config: RenderConfig,
63) -> Box<dyn FnMut(PortIndex) -> PortStyle + '_> {
64 let graph = h.portgraph();
65 Box::new(move |port| {
66 let node = graph.port_node(port).unwrap();
67 let optype = h.get_optype(h.get_node(node));
68 let offset = graph.port_offset(port).unwrap();
69 match optype.port_kind(offset).unwrap() {
70 EdgeKind::Function(pf) => PortStyle::new(html_escape::encode_text(&format!("{}", pf))),
71 EdgeKind::Const(ty) | EdgeKind::Value(ty) => {
72 PortStyle::new(html_escape::encode_text(&format!("{}", ty)))
73 }
74 EdgeKind::StateOrder => match graph.port_links(port).count() > 0 {
75 true => PortStyle::text("", false),
76 false => PortStyle::Hidden,
77 },
78 _ => PortStyle::text("", true),
79 }
80 })
81}
82
83#[allow(clippy::type_complexity)]
85pub(super) fn edge_style<H: HugrView + ?Sized>(
86 h: &H,
87 config: RenderConfig,
88) -> Box<
89 dyn FnMut(
90 <H::Portgraph<'_> as LinkView>::LinkEndpoint,
91 <H::Portgraph<'_> as LinkView>::LinkEndpoint,
92 ) -> EdgeStyle
93 + '_,
94> {
95 let graph = h.portgraph();
96 Box::new(move |src, tgt| {
97 let src_node = graph.port_node(src).unwrap();
98 let src_optype = h.get_optype(h.get_node(src_node));
99 let src_offset = graph.port_offset(src).unwrap();
100 let tgt_offset = graph.port_offset(tgt).unwrap();
101
102 let port_kind = src_optype.port_kind(src_offset).unwrap();
103
104 let style = match port_kind {
108 EdgeKind::StateOrder => EdgeStyle::Dotted,
109 EdgeKind::ControlFlow => EdgeStyle::Dashed,
110 EdgeKind::Const(_) | EdgeKind::Function(_) | EdgeKind::Value(_) => EdgeStyle::Solid,
111 };
112
113 fn type_label(e: EdgeKind) -> Option<String> {
115 match e {
116 EdgeKind::Const(ty) | EdgeKind::Value(ty) => Some(format!("{}", ty)),
117 EdgeKind::Function(pf) => Some(format!("{}", pf)),
118 _ => None,
119 }
120 }
121 let label = match (
124 config.port_offsets_in_edges,
125 type_label(port_kind).filter(|_| config.type_labels_in_edges),
126 ) {
127 (true, Some(ty)) => {
128 format!("{}:{}\n{ty}", src_offset.index(), tgt_offset.index())
129 }
130 (true, _) => format!("{}:{}", src_offset.index(), tgt_offset.index()),
131 (false, Some(ty)) => ty.to_string(),
132 _ => return style,
133 };
134 style.with_label(label)
135 })
136}