diff --git a/hugr-core/src/hugr/persistent/parents_view.rs b/hugr-core/src/hugr/persistent/parents_view.rs index 639acefccc..b4aa076060 100644 --- a/hugr-core/src/hugr/persistent/parents_view.rs +++ b/hugr-core/src/hugr/persistent/parents_view.rs @@ -5,7 +5,7 @@ use crate::{ extension::ExtensionRegistry, hugr::{ internal::HugrInternals, - views::{ExtractionResult, render::RenderConfig}, + views::{ExtractionResult, render}, }, ops::OpType, }; @@ -196,7 +196,8 @@ impl HugrView for ParentsView<'_> { unimplemented!() } - fn mermaid_string_with_config(&self, _config: RenderConfig) -> String { + #[allow(deprecated)] + fn mermaid_string_with_config(&self, _config: render::RenderConfig) -> String { unimplemented!() } diff --git a/hugr-core/src/hugr/persistent/trait_impls.rs b/hugr-core/src/hugr/persistent/trait_impls.rs index c11949db9a..6c68762029 100644 --- a/hugr-core/src/hugr/persistent/trait_impls.rs +++ b/hugr-core/src/hugr/persistent/trait_impls.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use itertools::{Either, Itertools}; -use portgraph::render::{DotFormat, MermaidFormat}; +use portgraph::render::MermaidFormat; use crate::{ Direction, Hugr, HugrView, Node, Port, @@ -10,7 +10,7 @@ use crate::{ internal::HugrInternals, views::{ ExtractionResult, - render::{self, RenderConfig}, + render::{self, MermaidFormatter, NodeLabel}, }, }, }; @@ -242,35 +242,49 @@ impl HugrView for PersistentHugr { .flat_map(move |port| self.linked_ports(node, port).map(|(opp_node, _)| opp_node)) } - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + #[allow(deprecated)] + fn mermaid_string_with_config(&self, config: render::RenderConfig) -> String { + self.mermaid_string_with_formatter(MermaidFormatter::from_render_config(config, self)) } - fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { // Extract a concrete HUGR for displaying let (hugr, node_map) = self.apply_all(); - // Map config accordingly - let config = RenderConfig { - entrypoint: config.entrypoint.map(|n| node_map[&n]), - node_indices: config.node_indices, - port_offsets_in_edges: config.port_offsets_in_edges, - type_labels_in_edges: config.type_labels_in_edges, - }; - // Render the extracted HUGR but map the node indices back to the // original patch node IDs - let inv_node_map: HashMap<_, _> = node_map.into_iter().map(|(k, v)| (v, k)).collect(); - let fmt_node_index = |n: portgraph::NodeIndex| format!("{:?}", inv_node_map[&n.into()]); + let entrypoint = formatter.entrypoint().map(|n| node_map[&n]); + let node_labels = match formatter.node_labels() { + NodeLabel::None => NodeLabel::None, + NodeLabel::Numeric => { + // replace node labels with patch node IDs + let node_labels_map: HashMap<_, _> = node_map + .into_iter() + .map(|(k, v)| (v, format!("{:?}", k))) + .collect(); + NodeLabel::Custom(node_labels_map) + } + NodeLabel::Custom(labels) => { + // rekey labels to the extracted HUGR node IDs + let labels = labels + .iter() + .map(|(k, v)| (node_map[k], v.clone())) + .collect(); + NodeLabel::Custom(labels) + } + }; + + // Map config accordingly + let config = MermaidFormatter::new(&hugr) + .with_entrypoint(entrypoint) + .with_node_labels(node_labels) + .with_port_offsets(formatter.port_offsets()) + .with_type_labels(formatter.type_labels()); + hugr.graph .mermaid_format() .with_hierarchy(&hugr.hierarchy) - .with_node_style(render::node_style(&hugr, config, fmt_node_index)) + .with_node_style(render::node_style(&hugr, config.clone())) .with_edge_style(render::edge_style(&hugr, config)) .finish() } @@ -279,26 +293,7 @@ impl HugrView for PersistentHugr { where Self: Sized, { - // Extract a concrete HUGR for displaying - let (hugr, node_map) = self.apply_all(); - - // Map config accordingly - let config = RenderConfig { - entrypoint: Some(node_map[&self.entrypoint()]), - ..RenderConfig::default() - }; - - // Render the extracted HUGR but map the node indices back to the - // original patch node IDs - let inv_node_map: HashMap<_, _> = node_map.into_iter().map(|(k, v)| (v, k)).collect(); - let fmt_node_index = |n: portgraph::NodeIndex| format!("{:?}", inv_node_map[&n.into()]); - hugr.graph - .dot_format() - .with_hierarchy(&hugr.hierarchy) - .with_node_style(render::node_style(&hugr, config, fmt_node_index)) - .with_port_style(render::port_style(&hugr, config)) - .with_edge_style(render::edge_style(&hugr, config)) - .finish() + unimplemented!("use mermaid_string instead") } fn extensions(&self) -> &crate::extension::ExtensionRegistry { @@ -351,19 +346,15 @@ mod tests { .try_extract_hugr([commit1, commit2, commit4]) .unwrap(); - let mermaid_str = hugr.mermaid_string_with_config(RenderConfig { - node_indices: false, - entrypoint: Some(hugr.entrypoint()), - ..Default::default() - }); + let mermaid_str = hugr + .mermaid_format() + .with_node_labels(NodeLabel::None) + .finish(); let extracted_hugr = hugr.to_hugr(); let exp_str = extracted_hugr - .mermaid_string_with_config(RenderConfig { - node_indices: false, - entrypoint: Some(extracted_hugr.entrypoint()), - ..Default::default() - }) - .to_string(); + .mermaid_format() + .with_node_labels(NodeLabel::None) + .finish(); assert_eq!(mermaid_str, exp_str); } diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index e86b024eb3..1704d79f65 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -14,7 +14,8 @@ use std::borrow::Cow; use std::collections::HashMap; pub use self::petgraph::PetgraphWrapper; -use self::render::RenderConfig; +#[allow(deprecated)] +use self::render::{MermaidFormatter, RenderConfig}; pub use rerooted::Rerooted; pub use root_checked::{InvalidSignature, RootCheckable, RootChecked, check_tag}; pub use sibling_subgraph::SiblingSubgraph; @@ -385,7 +386,9 @@ pub trait HugrView: HugrInternals { /// /// For a more detailed representation, use the [`HugrView::dot_string`] /// format instead. - fn mermaid_string(&self) -> String; + fn mermaid_string(&self) -> String { + self.mermaid_string_with_formatter(self.mermaid_format()) + } /// Return the mermaid representation of the underlying hierarchical graph. /// @@ -394,8 +397,51 @@ pub trait HugrView: HugrInternals { /// /// For a more detailed representation, use the [`HugrView::dot_string`] /// format instead. + #[deprecated(note = "Use `mermaid_format` instead")] + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: RenderConfig) -> String; + /// Return the mermaid representation of the underlying hierarchical graph + /// according to the provided [`MermaidFormatter`] formatting options. + /// + /// The hierarchy is represented using subgraphs. Edges are labelled with + /// their source and target ports. + /// + /// For a more detailed representation, use the [`HugrView::dot_string`] + /// format instead. + /// + /// ## Deprecation of [`RenderConfig`] + /// While the deprecated [HugrView::mermaid_string_with_config] exists, this + /// will by default try to convert the formatter options to a [`RenderConfig`], + /// but this may panic if the configuration is not supported. Users are + /// encouraged to provide an implementation of this method overriding the default + /// and no longer rely on [HugrView::mermaid_string_with_config]. + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { + #[allow(deprecated)] + let config = match RenderConfig::try_from(formatter) { + Ok(config) => config, + Err(e) => { + panic!("Unsupported format option: {}", e); + } + }; + #[allow(deprecated)] + self.mermaid_string_with_config(config) + } + + /// Construct a mermaid representation of the underlying hierarchical graph. + /// + /// Options can be set on the returned [`MermaidFormatter`] struct, before + /// generating the String with [`MermaidFormatter::finish`]. + /// + /// The hierarchy is represented using subgraphs. Edges are labelled with + /// their source and target ports. + /// + /// For a more detailed representation, use the [`HugrView::dot_string`] + /// format instead. + fn mermaid_format(&self) -> MermaidFormatter { + MermaidFormatter::new(self).with_entrypoint(self.entrypoint()) + } + /// Return the graphviz representation of the underlying graph and hierarchy side by side. /// /// For a simpler representation, use the [`HugrView::mermaid_string`] format instead. @@ -629,21 +675,17 @@ impl HugrView for Hugr { self.graph.all_neighbours(node.into_portgraph()).map_into() } - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + #[allow(deprecated)] + fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + self.mermaid_string_with_formatter(MermaidFormatter::from_render_config(config, self)) } - fn mermaid_string_with_config(&self, config: RenderConfig) -> String { + fn mermaid_string_with_formatter(&self, formatter: MermaidFormatter) -> String { self.graph .mermaid_format() .with_hierarchy(&self.hierarchy) - .with_node_style(render::node_style(self, config, |n| n.index().to_string())) - .with_edge_style(render::edge_style(self, config)) + .with_node_style(render::node_style(self, formatter.clone())) + .with_edge_style(render::edge_style(self, formatter)) .finish() } @@ -651,16 +693,13 @@ impl HugrView for Hugr { where Self: Sized, { - let config = RenderConfig { - entrypoint: Some(self.entrypoint()), - ..RenderConfig::default() - }; + let formatter = MermaidFormatter::new(self).with_entrypoint(self.entrypoint()); self.graph .dot_format() .with_hierarchy(&self.hierarchy) - .with_node_style(render::node_style(self, config, |n| n.index().to_string())) - .with_port_style(render::port_style(self, config)) - .with_edge_style(render::edge_style(self, config)) + .with_node_style(render::node_style(self, formatter.clone())) + .with_port_style(render::port_style(self)) + .with_edge_style(render::edge_style(self, formatter)) .finish() } diff --git a/hugr-core/src/hugr/views/impls.rs b/hugr-core/src/hugr/views/impls.rs index 09c4475be8..75311f2f73 100644 --- a/hugr-core/src/hugr/views/impls.rs +++ b/hugr-core/src/hugr/views/impls.rs @@ -58,7 +58,9 @@ macro_rules! hugr_view_methods { fn neighbours(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator + Clone; fn all_neighbours(&self, node: Self::Node) -> impl Iterator + Clone; fn mermaid_string(&self) -> String; + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: crate::hugr::views::render::RenderConfig) -> String; + fn mermaid_string_with_formatter(&self, #[into] formatter: crate::hugr::views::render::MermaidFormatter) -> String; fn dot_string(&self) -> String; fn static_source(&self, node: Self::Node) -> Option; fn static_targets(&self, node: Self::Node) -> Option>; diff --git a/hugr-core/src/hugr/views/render.rs b/hugr-core/src/hugr/views/render.rs index 9d597f1c04..b787a9e383 100644 --- a/hugr-core/src/hugr/views/render.rs +++ b/hugr-core/src/hugr/views/render.rs @@ -1,16 +1,23 @@ //! Helper methods to compute the node/edge/port style when rendering a HUGR //! into dot or mermaid format. +use std::collections::HashMap; + use portgraph::render::{EdgeStyle, NodeStyle, PortStyle, PresentationStyle}; use portgraph::{LinkView, MultiPortGraph, NodeIndex, PortIndex, PortView}; +use crate::core::HugrNode; +use crate::hugr::internal::HugrInternals; use crate::ops::{NamedOp, OpType}; use crate::types::EdgeKind; use crate::{Hugr, HugrView, Node}; -/// Configuration for rendering a HUGR graph. +/// Reduced configuration for rendering a HUGR graph. +/// +/// Additional options are available in the [`MermaidFormatter`] struct. #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[non_exhaustive] +#[deprecated(note = "Use `MermaidFormatter` instead")] pub struct RenderConfig { /// Show the node index in the graph nodes. pub node_indices: bool, @@ -22,6 +29,213 @@ pub struct RenderConfig { pub entrypoint: Option, } +/// Configuration for rendering a HUGR graph. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MermaidFormatter<'h, H: HugrInternals + ?Sized = Hugr> { + /// The HUGR to render. + hugr: &'h H, + /// How to display the node indices. + node_labels: NodeLabel, + /// Show port offsets in the graph edges. + port_offsets_in_edges: bool, + /// Show type labels on edges. + type_labels_in_edges: bool, + /// A node to highlight as the graph entrypoint. + entrypoint: Option, +} + +impl<'h, H: HugrInternals + ?Sized> MermaidFormatter<'h, H> { + /// Create a new [`MermaidFormatter`] from a [`RenderConfig`]. + #[allow(deprecated)] + pub fn from_render_config(config: RenderConfig, hugr: &'h H) -> Self { + let node_labels = if config.node_indices { + NodeLabel::Numeric + } else { + NodeLabel::None + }; + Self { + hugr, + node_labels, + port_offsets_in_edges: config.port_offsets_in_edges, + type_labels_in_edges: config.type_labels_in_edges, + entrypoint: config.entrypoint, + } + } + + /// Create a new [`MermaidFormatter`] for the given [`Hugr`]. + pub fn new(hugr: &'h H) -> Self { + Self { + hugr, + node_labels: NodeLabel::Numeric, + port_offsets_in_edges: true, + type_labels_in_edges: true, + entrypoint: None, + } + } + + /// The entrypoint to highlight in the rendered graph. + pub fn entrypoint(&self) -> Option { + self.entrypoint + } + + /// The rendering style of the node labels. + pub fn node_labels(&self) -> &NodeLabel { + &self.node_labels + } + + /// Whether to show port offsets on edges. + pub fn port_offsets(&self) -> bool { + self.port_offsets_in_edges + } + + /// Whether to show type labels on edges. + pub fn type_labels(&self) -> bool { + self.type_labels_in_edges + } + + /// Set the node labels style. + pub fn with_node_labels(mut self, node_labels: NodeLabel) -> Self { + self.node_labels = node_labels; + self + } + + /// Set whether to show port offsets in edges. + pub fn with_port_offsets(mut self, show: bool) -> Self { + self.port_offsets_in_edges = show; + self + } + + /// Set whether to show type labels in edges. + pub fn with_type_labels(mut self, show: bool) -> Self { + self.type_labels_in_edges = show; + self + } + + /// Set the entrypoint node to highlight. + pub fn with_entrypoint(mut self, entrypoint: impl Into>) -> Self { + self.entrypoint = entrypoint.into(); + self + } + + /// Render the graph into a Mermaid string. + pub fn finish(self) -> String + where + H: HugrView, + { + self.hugr.mermaid_string_with_formatter(self) + } + + pub(crate) fn with_hugr>( + self, + hugr: &NewH, + ) -> MermaidFormatter<'_, NewH> { + let MermaidFormatter { + hugr: _, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = self; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } +} + +/// An error that occurs when trying to convert a `FullRenderConfig` into a +/// `RenderConfig`. +#[derive(Debug, thiserror::Error)] +pub enum UnsupportedRenderConfig { + /// Custom node labels are not supported in the `RenderConfig` struct. + #[error("Custom node labels are not supported in the `RenderConfig` struct")] + CustomNodeLabels, +} + +#[allow(deprecated)] +impl<'h, H: HugrInternals + ?Sized> TryFrom> for RenderConfig { + type Error = UnsupportedRenderConfig; + + fn try_from(value: MermaidFormatter<'h, H>) -> Result { + if matches!(value.node_labels, NodeLabel::Custom(_)) { + return Err(UnsupportedRenderConfig::CustomNodeLabels); + } + let node_indices = matches!(value.node_labels, NodeLabel::Numeric); + Ok(Self { + node_indices, + port_offsets_in_edges: value.port_offsets_in_edges, + type_labels_in_edges: value.type_labels_in_edges, + entrypoint: value.entrypoint, + }) + } +} + +macro_rules! impl_mermaid_formatter_from { + ($t:ty, $($lifetime:tt)?) => { + impl<'h, $($lifetime,)? H: HugrView> From> for MermaidFormatter<'h, H> { + fn from(value: MermaidFormatter<'h, $t>) -> Self { + let MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = value; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } + } + }; +} + +impl_mermaid_formatter_from!(&'hh H, 'hh); +impl_mermaid_formatter_from!(&'hh mut H, 'hh); +impl_mermaid_formatter_from!(std::rc::Rc,); +impl_mermaid_formatter_from!(std::sync::Arc,); +impl_mermaid_formatter_from!(Box,); + +impl<'h, H: HugrView + ToOwned> From>> + for MermaidFormatter<'h, H> +{ + fn from(value: MermaidFormatter<'h, std::borrow::Cow<'_, H>>) -> Self { + let MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } = value; + MermaidFormatter { + hugr, + node_labels, + port_offsets_in_edges, + type_labels_in_edges, + entrypoint, + } + } +} + +/// How to display the node indices. +#[derive(Default, Clone, Debug, PartialEq, Eq)] +pub enum NodeLabel { + /// Do not display the node index. + None, + /// Display the node index as a number. + #[default] + Numeric, + /// Display the labels corresponding to the node indices. + Custom(HashMap), +} + +#[allow(deprecated)] impl Default for RenderConfig { fn default() -> Self { Self { @@ -36,8 +250,7 @@ impl Default for RenderConfig { /// Formatter method to compute a node style. pub(in crate::hugr) fn node_style<'a>( h: &'a Hugr, - config: RenderConfig, - fmt_node_index: impl Fn(NodeIndex) -> String + 'a, + formatter: MermaidFormatter<'a>, ) -> Box NodeStyle + 'a> { fn node_name(h: &Hugr, n: NodeIndex) -> String { match h.get_optype(n.into()) { @@ -50,42 +263,54 @@ pub(in crate::hugr) fn node_style<'a>( let mut entrypoint_style = PresentationStyle::default(); entrypoint_style.stroke = Some("#832561".to_string()); entrypoint_style.stroke_width = Some("3px".to_string()); - let entrypoint = config.entrypoint.map(Node::into_portgraph); + let entrypoint = formatter.entrypoint.map(Node::into_portgraph); - if config.node_indices { - Box::new(move |n| { + match formatter.node_labels { + NodeLabel::Numeric => Box::new(move |n| { if Some(n) == entrypoint { NodeStyle::boxed(format!( "({ni}) [**{name}**]", - ni = fmt_node_index(n), + ni = n.index(), name = node_name(h, n) )) .with_attrs(entrypoint_style.clone()) } else { NodeStyle::boxed(format!( "({ni}) {name}", - ni = fmt_node_index(n), + ni = n.index(), name = node_name(h, n) )) } - }) - } else { - Box::new(move |n| { + }), + NodeLabel::None => Box::new(move |n| { if Some(n) == entrypoint { NodeStyle::boxed(format!("[**{name}**]", name = node_name(h, n))) .with_attrs(entrypoint_style.clone()) } else { NodeStyle::boxed(node_name(h, n)) } - }) + }), + NodeLabel::Custom(labels) => Box::new(move |n| { + if Some(n) == entrypoint { + NodeStyle::boxed(format!( + "({label}) [**{name}**]", + label = labels.get(&n.into()).unwrap_or(&n.index().to_string()), + name = node_name(h, n) + )) + .with_attrs(entrypoint_style.clone()) + } else { + NodeStyle::boxed(format!( + "({label}) {name}", + label = labels.get(&n.into()).unwrap_or(&n.index().to_string()), + name = node_name(h, n) + )) + } + }), } } /// Formatter method to compute a port style. -pub(in crate::hugr) fn port_style( - h: &Hugr, - _config: RenderConfig, -) -> Box PortStyle + '_> { +pub(in crate::hugr) fn port_style(h: &Hugr) -> Box PortStyle + '_> { let graph = &h.graph; Box::new(move |port| { let node = graph.port_node(port).unwrap(); @@ -110,15 +335,15 @@ pub(in crate::hugr) fn port_style( /// Formatter method to compute an edge style. #[allow(clippy::type_complexity)] -pub(in crate::hugr) fn edge_style( - h: &Hugr, - config: RenderConfig, +pub(in crate::hugr) fn edge_style<'a>( + h: &'a Hugr, + config: MermaidFormatter<'_>, ) -> Box< dyn FnMut( ::LinkEndpoint, ::LinkEndpoint, ) -> EdgeStyle - + '_, + + 'a, > { let graph = &h.graph; Box::new(move |src, tgt| { @@ -162,3 +387,45 @@ pub(in crate::hugr) fn edge_style( style.with_label(label) }) } + +#[cfg(test)] +mod tests { + use crate::{NodeIndex, builder::test::simple_dfg_hugr}; + + use super::*; + + #[cfg_attr(miri, ignore)] // Opening files is not supported in (isolated) miri + #[test] + fn test_custom_node_labels() { + let h = simple_dfg_hugr(); + let node_labels = h + .nodes() + .map(|n| (n, format!("node_{}", n.index()))) + .collect(); + let config = h + .mermaid_format() + .with_node_labels(NodeLabel::Custom(node_labels)); + insta::assert_snapshot!(h.mermaid_string_with_formatter(config)); + } + + #[test] + fn convert_full_render_config_to_render_config() { + let h = simple_dfg_hugr(); + let config: MermaidFormatter = + MermaidFormatter::new(&h).with_node_labels(NodeLabel::Custom(HashMap::new())); + #[allow(deprecated)] + { + assert!(RenderConfig::try_from(config).is_err()); + } + + #[allow(deprecated)] + let config = RenderConfig { + entrypoint: Some(h.entrypoint()), + ..Default::default() + }; + assert_eq!( + MermaidFormatter::from_render_config(config, &h), + h.mermaid_format() + ) + } +} diff --git a/hugr-core/src/hugr/views/rerooted.rs b/hugr-core/src/hugr/views/rerooted.rs index ea849372db..8c84abdc71 100644 --- a/hugr-core/src/hugr/views/rerooted.rs +++ b/hugr-core/src/hugr/views/rerooted.rs @@ -4,7 +4,6 @@ use crate::hugr::HugrMut; use crate::hugr::internal::{HugrInternals, HugrMutInternals}; -use super::render::RenderConfig; use super::{HugrView, panic_invalid_node}; /// A HUGR wrapper with a modified entrypoint node. @@ -60,14 +59,12 @@ impl HugrView for Rerooted { self.hugr.get_optype(self.entrypoint) } - #[inline] - fn mermaid_string(&self) -> String { - self.mermaid_string_with_config(RenderConfig { - node_indices: true, - port_offsets_in_edges: true, - type_labels_in_edges: true, - entrypoint: Some(self.entrypoint()), - }) + fn mermaid_string_with_formatter( + &self, + formatter: crate::hugr::views::render::MermaidFormatter, + ) -> String { + self.hugr + .mermaid_string_with_formatter(formatter.with_hugr(&self.hugr)) } delegate::delegate! { @@ -103,6 +100,7 @@ impl HugrView for Rerooted { fn first_child(&self, node: Self::Node) -> Option; fn neighbours(&self, node: Self::Node, dir: crate::Direction) -> impl Iterator + Clone; fn all_neighbours(&self, node: Self::Node) -> impl Iterator + Clone; + #[allow(deprecated)] fn mermaid_string_with_config(&self, config: crate::hugr::views::render::RenderConfig) -> String; fn dot_string(&self) -> String; fn static_source(&self, node: Self::Node) -> Option; @@ -235,4 +233,12 @@ mod test { ); assert!(extracted_hugr.get_optype(extracted_bb).is_dataflow_block()); } + + #[test] + fn mermaid_format() { + let h = simple_cfg_hugr(); + let rerooted = h.with_entrypoint(h.entrypoint()); + let mermaid_str = rerooted.mermaid_format().finish(); + assert_eq!(mermaid_str, h.mermaid_format().finish()); + } } diff --git a/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap b/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap new file mode 100644 index 0000000000..22b2ed8a51 --- /dev/null +++ b/hugr-core/src/hugr/views/snapshots/hugr_core__hugr__views__render__tests__custom_node_labels.snap @@ -0,0 +1,22 @@ +--- +source: hugr-core/src/hugr/views/render.rs +expression: h.mermaid_string_with_formatter(config) +--- +graph LR + subgraph 0 ["(node_0) Module"] + direction LR + subgraph 1 ["(node_1) FuncDefn: #quot;main#quot;"] + direction LR + 2["(node_2) Input"] + 3["(node_3) Output"] + subgraph 4 ["(node_4) [**DFG**]"] + direction LR + style 4 stroke:#832561,stroke-width:3px + 5["(node_5) Input"] + 6["(node_6) Output"] + 5--"0:0
Bool"-->6 + end + 2--"0:0
Bool"-->4 + 4--"0:0
Bool"-->3 + end + end