From 4c067093ac34fc9e32227bc5bc853d47e9220540 Mon Sep 17 00:00:00 2001 From: Test Date: Sun, 22 Mar 2026 11:20:02 +0100 Subject: [PATCH] feat(etch): type_shapes provider for custom SVG node shapes SvgOptions now accepts a type_shapes HashMap mapping node types to custom SVG shape functions. The shape function receives (node_type, x, y, width, height, fill, stroke) and returns raw SVG element string. Falls back to default rect when no provider is registered. Trace: skip Co-Authored-By: Claude Opus 4.6 (1M context) --- etch/src/svg.rs | 67 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/etch/src/svg.rs b/etch/src/svg.rs index b0bbb95..ff7460a 100644 --- a/etch/src/svg.rs +++ b/etch/src/svg.rs @@ -12,11 +12,23 @@ use crate::layout::GraphLayout; // Public types // --------------------------------------------------------------------------- +/// SVG shape element for a node — replaces the default ``. +/// +/// The shape provider returns raw SVG element(s) as a string. +/// The string is inserted directly into the SVG `` for the node, +/// replacing the default rectangle. +pub type ShapeProvider = Box String + Send + Sync>; + /// Options that control SVG rendering. -#[derive(Debug, Clone)] pub struct SvgOptions { /// Map from node type to fill colour (CSS value). pub type_colors: HashMap, + /// Map from node type to custom SVG shape provider. + /// + /// The function receives `(node_type, x, y, width, height, fill, stroke_color)` + /// and returns raw SVG element string (e.g. ``). + /// If no provider is set for a type, the default `` is used. + pub type_shapes: HashMap, /// Font family for all text. pub font_family: String, /// Font size in px. @@ -43,6 +55,7 @@ impl Default for SvgOptions { fn default() -> Self { Self { type_colors: HashMap::new(), + type_shapes: HashMap::new(), font_family: "system-ui, -apple-system, sans-serif".into(), font_size: 13.0, padding: 20.0, @@ -57,6 +70,20 @@ impl Default for SvgOptions { } } +// SvgOptions can't derive Debug/Clone due to Box +impl std::fmt::Debug for SvgOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SvgOptions") + .field("type_colors", &self.type_colors) + .field( + "type_shapes", + &format!("<{} providers>", self.type_shapes.len()), + ) + .field("font_family", &self.font_family) + .finish() + } +} + // --------------------------------------------------------------------------- // Public entry point // --------------------------------------------------------------------------- @@ -142,7 +169,7 @@ fn write_style(svg: &mut String, options: &SvgOptions) { write!( svg, "