From ad35f289c7430a9f15b7487553c880e9349bd941 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Sat, 25 Jan 2025 13:35:08 +0100 Subject: [PATCH] back references for visnodes --- bank.svg | 15 ++++ examples/bank_architecture/bank.vis | 3 + output_multiple_nodes.svg | 4 +- src/.DS_Store | Bin 0 -> 6148 bytes src/lib.rs | 122 +++++++++++++++++++++------- src/parse/parser.rs | 66 +++++++++------ src/render/svg_renderer.rs | 101 +++++++++++------------ src/render/svglib/circle.rs | 20 +++-- src/render/svglib/ellipse.rs | 22 +++-- src/render/svglib/foreign_object.rs | 30 ++++--- src/render/svglib/group.rs | 42 +++++----- src/render/svglib/image.rs | 20 +++-- src/render/svglib/line.rs | 22 +++-- src/render/svglib/link.rs | 20 +++-- src/render/svglib/mod.rs | 3 +- src/render/svglib/path.rs | 20 +++-- src/render/svglib/rect.rs | 21 +++-- src/render/svglib/svg.rs | 12 +-- src/render/svglib/text.rs | 31 +++---- 19 files changed, 354 insertions(+), 220 deletions(-) create mode 100644 bank.svg create mode 100644 src/.DS_Store diff --git a/bank.svg b/bank.svg new file mode 100644 index 0000000..8a17733 --- /dev/null +++ b/bank.svg @@ -0,0 +1,15 @@ +Calculation />Account interest Calculation />Interest Rates />Configuration />NoB Execution />Collection of Reinstatement instructions />NoB />Reporting />Bank Motor />Bank Scripts />Bank Client />Bank DB />Bank />InterestEngine /> \ No newline at end of file diff --git a/examples/bank_architecture/bank.vis b/examples/bank_architecture/bank.vis index fb33805..77f3050 100644 --- a/examples/bank_architecture/bank.vis +++ b/examples/bank_architecture/bank.vis @@ -28,6 +28,9 @@ structure { } styles { + structure(group){ + orientation: vertical + } lanes(group) { type: textnode orientation: horizontal diff --git a/output_multiple_nodes.svg b/output_multiple_nodes.svg index 8ad8a2e..246734f 100644 --- a/output_multiple_nodes.svg +++ b/output_multiple_nodes.svg @@ -1,4 +1,4 @@ -Node 1longer />Node 2 />Node 3is longerthan you are to me /> \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..0b0ace9cc13ea946354c994b6c757aa8a982b042 GIT binary patch literal 6148 zcmeHKI}XAy47C9vBo;=-T!9-z2u{ES7(gtjNR?pj#JM;c&!43fHdxTJvwib{jWVJ@ILbvFO*u{@0mP=P;Cz`hR!Zden$K>u`L@D>0#LD&s*?GNs z1tJ2|paO%c*Cld`rB9%r?-!1r*=dBDvucM1kC$3QQ~SXen;dQ#*Sn`6Hw Uc7aYu-0496445u7D)4Iso`&fao&W#< literal 0 HcmV?d00001 diff --git a/src/lib.rs b/src/lib.rs index 3dd8805..816299d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,61 +1,125 @@ pub mod parse; pub mod render; -use crate::Element::{Edge, Node}; -use parse::tokens::TokenType; +use crate::parse::tokens::TokenType; use std::collections::HashMap; use std::fmt::Display; -#[derive(Debug)] pub struct Vis { - pub structure: Vec, + pub structure: VisNode, pub styles: Vec, } +impl Vis { + pub fn get_node(&self, id: &str) -> Option<&VisNode> { + if self.structure.id == id { + return Some(&self.structure); + } else { + for child in &self.structure.children { + if child.id == id { + return Some(child); + } + } + } + None + } + + pub fn get_style(&self, node: &VisNode) -> Option<&StyleNode> { + let style = self.styles.iter().find(|s| s.id_ref == node.id); + if style.is_none() && node.id != "structure" { + for child in &node.children { + if let Some(style) = self.get_style(child) { + return Some(style); + } + } + None + } else { + None + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub enum NodeType { + Node, + Edge, +} + #[derive(Debug, Clone)] -pub enum Element { - Node(String, Option, Vec), - Edge(String, String, TokenType, Option), +pub struct VisNode { + pub node_type: NodeType, + pub id: String, + targetid: Option, + pub label: Option, + pub children: Vec, + pub parent: Option, + edgetype: Option, } -impl Element { - pub fn new_node(id: &str, label: Option<&str>, children: Vec) -> Element { - Element::Node(id.into(), label.map(|s| s.into()), children) +impl VisNode { + pub fn new_node( + id: impl Into, + label: Option>, + children: Vec, + ) -> Self { + Self { + id: id.into(), + targetid: None, + label: label.map(|s| s.into()), + children, + parent: None, + node_type: NodeType::Node, + edgetype: None, + } } + pub fn new_edge( - id: String, - source: String, - token: TokenType, - label: Option, - ) -> Element { - Edge(id, source, token, label) + sourceid: impl Into, + targetid: impl Into, + edgetype: TokenType, + label: Option>, + ) -> Self { + Self { + id: sourceid.into(), + targetid: Some(targetid.into()), + edgetype: Some(edgetype), + label: label.map(|l| l.into()), + children: vec![], + parent: None, + node_type: NodeType::Edge, + } } } -impl Display for Element { +impl Display for VisNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Node(id, label, children) => { + match &self.node_type { + NodeType::Node => { let mut string = String::new(); string.push_str(&format!( "Node {{{}: {}", - id, - label.as_ref().unwrap_or(&"".to_string()) + self.id, + self.label.as_ref().unwrap_or(&"".to_string()) )); - for child in children { + for child in &self.children { string.push_str(&format!(" {}", child)); } string.push('}'); write!(f, "{}", string) } - Edge(id, source, token, label) => { - let mut string = "Edge {{".to_string(); - string.push_str(&format!("{} {} {:?}", id, source, token)); - if let Some(label) = label { - string.push_str(&format!(" {}", label)); + NodeType::Edge => { + write!(f, "Edge {{")?; + write!( + f, + "{} {} {:?}", + self.id, + self.targetid.as_ref().unwrap(), + self.edgetype + )?; + if let Some(label) = &self.label { + write!(f, " {}", label)?; } - string.push('}'); - write!(f, "{}", string) + write!(f, "}}") } } } diff --git a/src/parse/parser.rs b/src/parse/parser.rs index e6462a0..6fd31a3 100644 --- a/src/parse/parser.rs +++ b/src/parse/parser.rs @@ -3,7 +3,7 @@ use crate::{ Token, TokenType::{self, *}, }, - ContainerType, Element, StyleNode, Vis, + ContainerType, StyleNode, Vis, VisNode, }; use anyhow::anyhow; use std::collections::HashMap; @@ -13,10 +13,21 @@ pub fn parse_vis(contents: &str) -> anyhow::Result { // println!("{:?}", tokens); let mut parser = Parser::new(tokens); - Ok(Vis { - structure: parser.structure()?, + let structure = parser.structure()?; + + let mut vis = Vis { + structure: VisNode::new_node("structure", None::, structure), styles: parser.styles()?, - }) + }; + + // add bottom up references (sorry no actual refs, but clones) + vis.structure.children.iter_mut().for_each(|node| { + let c = node.clone(); + node.children.iter_mut().for_each(|child| { + child.parent.replace(c.id.to_owned()); + }); + }); + Ok(vis) } struct Parser { @@ -29,7 +40,7 @@ impl Parser { Self { tokens, current: 0 } } - fn structure(&mut self) -> anyhow::Result> { + fn structure(&mut self) -> anyhow::Result> { if self.match_token(Structure) { self.elements() } else { @@ -37,7 +48,7 @@ impl Parser { } } - fn elements(&mut self) -> anyhow::Result> { + fn elements(&mut self) -> anyhow::Result> { // println!("nodes {:?}", self.peek()); self.consume(LeftBrace, "Expected '{'")?; let mut nodes = vec![]; @@ -47,7 +58,7 @@ impl Parser { Ok(nodes) } - fn element(&mut self) -> anyhow::Result { + fn element(&mut self) -> anyhow::Result { // println!("node {:?}", self.peek()); let id = self.id()?; // println!("id {}", id); @@ -68,14 +79,14 @@ impl Parser { vec![] }; - Ok(Element::Node(id, title, children)) + Ok(VisNode::new_node(id, title, children)) } } - fn edge(&mut self, from_id: String, arrow: Token) -> anyhow::Result { + fn edge(&mut self, from_id: String, arrow: Token) -> anyhow::Result { let to_id = self.id()?; let title = self.title()?; - Ok(Element::Edge(from_id, to_id, arrow.tokentype, title)) + Ok(VisNode::new_edge(from_id, to_id, arrow.tokentype, title)) } fn title(&mut self) -> anyhow::Result> { @@ -119,8 +130,13 @@ impl Parser { } fn style(&mut self) -> anyhow::Result { - if self.check(&Identifier) { - let idref = self.peek().lexeme.to_owned(); + if self.check(&Identifier) || self.check(&Structure) { + let idref = if self.check(&Structure) { + // only structure element can also be referenced + "structure".to_string() + } else { + self.peek().lexeme.to_owned() + }; self.advance(); let containertype = self.containertype()?; self.consume(RightParen, "Expected ')'")?; @@ -227,7 +243,7 @@ impl Parser { #[cfg(test)] mod tests { - use crate::Element; + use crate::NodeType::Node; #[test] fn test_parse() { @@ -244,28 +260,32 @@ mod tests { if let Some(vis) = vis { // Validate the parsed structure by asserting its elements assert_eq!( - vis.structure.len(), + vis.structure.children.len(), 1, "The structure should contain one top-level element" ); - if let Element::Node(id, title, children) = &vis.structure[0] { - assert_eq!(id, "top", "The ID of the first node should be 'top'"); + if vis.structure.children[0].node_type == Node { + let node = &vis.structure.children[0]; + assert_eq!(node.id, "top", "The ID of the first node should be 'top'"); assert_eq!( - title.as_ref().unwrap(), + node.label.as_ref().unwrap(), &"top-node".to_owned(), "The title of the first node should be 'top-node'" ); - assert_eq!(children.len(), 1); - let child = &children[0]; - if let Element::Node(id, title, children) = child { - assert_eq!(id, "child", "The ID of the second node should be 'child'"); + assert_eq!(node.children.len(), 1); + let child = &node.children[0]; + if child.node_type == Node { assert_eq!( - title.as_ref().unwrap(), + child.id, "child", + "The ID of the second node should be 'child'" + ); + assert_eq!( + child.label.as_ref().unwrap(), &"child-node".to_owned(), "The title of the second node should be 'child-node'" ); - assert_eq!(children.len(), 0); + assert_eq!(child.children.len(), 0); } } else { panic!("The top-level element should be a Node"); diff --git a/src/render/svg_renderer.rs b/src/render/svg_renderer.rs index 50a6293..fd9c7ae 100644 --- a/src/render/svg_renderer.rs +++ b/src/render/svg_renderer.rs @@ -2,8 +2,7 @@ use crate::render::svglib::rect::rect; use crate::render::svglib::svg::{svg, Svg}; use crate::render::svglib::text::text; use crate::render::Renderer; -use crate::Element::Node; -use crate::{Element, StyleNode, Vis}; +use crate::{StyleNode, Vis, VisNode}; pub struct SvgRender; @@ -15,7 +14,7 @@ impl Renderer for SvgRender { svg.height(100); let style = include_str!("svg_node.css"); svg.style(style); - let (w, h) = render_elements(&mut svg, &vis.structure, &vis.styles, 0, 0); + let (w, h) = render_elements(&mut svg, &vis.structure.children, &vis.styles, 0, 0); svg.viewbox(format!("0 0 {} {}", w, h)); Ok(svg.to_string().into_bytes()) } @@ -23,7 +22,7 @@ impl Renderer for SvgRender { fn render_elements( svg: &mut Svg, - elements: &Vec, + elements: &[VisNode], _styles: &Vec, start_x: u32, start_y: u32, @@ -36,43 +35,41 @@ fn render_elements( let y = start_y; for element in elements { - if let Node(id, label, children) = element { - let (width, height) = if !children.is_empty() { - render_elements(svg, children, _styles, x + padding, y + padding) - } else if let Node(_, Some(label), _) = element { - let label_width = label.lines().map(|l| l.len()).max().unwrap_or(0) as u32; - let label_height = label.lines().count() as u32; - ((label_width as f32 * 4.9 + 8.0) as u32, label_height * 15) - } else { - (0, 0) - }; + let (width, height) = if !element.children.is_empty() { + render_elements(svg, &element.children, _styles, x + padding, y + padding) + } else if let Some(label) = element.label.as_ref() { + let label_width = label.lines().map(|l| l.len()).max().unwrap_or(0) as u32; + let label_height = label.lines().count() as u32; + ((label_width as f32 * 4.9 + 8.0) as u32, label_height * 15) + } else { + (0, 0) + }; - total_width += width + padding; - total_height = u32::max(total_height, height + padding); + total_width += width + padding; + total_height = u32::max(total_height, height + padding); - svg.add( - rect() - .id(id) - .x(x + padding) - .y(y + padding) - .width(width) - .height(height) - .stroke("white") - .fill("none"), - ); + svg.add( + rect() + .id(element.id.clone()) + .x(x + padding) + .y(y + padding) + .width(width) + .height(height) + .stroke("white") + .fill("none"), + ); - svg.add( - text() - .x(x + padding + width / 2) - .y(start_y + padding) - .fill("white") - .text(label.as_ref().map(|l| l.as_str()).unwrap_or("")) - .attr("text-anchor", "middle") - .attr("stroke-width", "1") - .class("node"), - ); - x += width + padding; - } + svg.add( + text() + .x(x + padding + width / 2) + .y(start_y + padding) + .fill("white") + .text(element.label.as_ref().unwrap_or(&"".to_string())) + .attr("text-anchor", "middle") + .attr("stroke-width", "1") + .class("node"), + ); + x += width + padding; } (total_width + padding, total_height + padding) @@ -81,7 +78,7 @@ fn render_elements( #[cfg(test)] mod tests { use super::*; - use crate::{ContainerType, StyleNode, Vis}; + use crate::{ContainerType, StyleNode, Vis, VisNode}; use std::fs::File; use std::io::Write; @@ -98,32 +95,32 @@ mod tests { }; // Create mock Elements - let element1 = Node( - "node1".to_string(), - Some("Node 1\nlonger".to_string()), + let element1 = VisNode::new_node( + "node1", + Some("Node 1\nlonger"), vec![], // No child elements ); - let element2 = Node( - "node2".to_string(), - Some("Node 2".to_string()), + let element2 = VisNode::new_node( + "node2", + Some("Node 2"), vec![], // No child elements ); - let element3 = Node( - "node3".to_string(), - Some("Node 3\nis longer\nthan you are to me".to_string()), + let element3 = VisNode::new_node( + "node3", + Some("Node 3\nis longer\nthan you are to me"), vec![], // No child elements ); - let root = Node( - "root".to_string(), - Some("root".to_string()), + let root = VisNode::new_node( + "structure", + Some("root"), vec![element1, element2, element3], ); // Create Vis structure let vis = Vis { styles: vec![style_node], - structure: vec![root], + structure: root, }; let svg_output = SvgRender {}.render(vis).unwrap(); diff --git a/src/render/svglib/circle.rs b/src/render/svglib/circle.rs index 551ef15..7632863 100644 --- a/src/render/svglib/circle.rs +++ b/src/render/svglib/circle.rs @@ -1,4 +1,5 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, SvgElement, ElementType, Shape, Value}; +use std::fmt::Display; pub fn circle() -> Circle { Circle::default() @@ -43,17 +44,20 @@ impl Shape for Circle { } } -impl Element for Circle { - fn get_type(&self) -> ElementType { - ElementType::Circle - } - - fn to_string(&self) -> String { - format!( +impl Display for Circle { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, r#""#, self.0.iter().map(att_to_string).collect::() ) } +} + +impl SvgElement for Circle { + fn get_type(&self) -> ElementType { + ElementType::Circle + } fn atts(&self) -> &[Att] { &self.0 diff --git a/src/render/svglib/ellipse.rs b/src/render/svglib/ellipse.rs index 94a8a6f..20106ef 100644 --- a/src/render/svglib/ellipse.rs +++ b/src/render/svglib/ellipse.rs @@ -1,4 +1,5 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, ElementType, Shape, SvgElement, Value}; +use std::fmt::Display; pub fn ellipse() -> Ellipse { Ellipse(vec![]) @@ -46,7 +47,17 @@ impl Shape for Ellipse { } } -impl Element for Ellipse { +impl Display for Ellipse { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + r#""#, + self.0.iter().map(att_to_string).collect::() + ) + } +} + +impl SvgElement for Ellipse { fn get_type(&self) -> ElementType { ElementType::Ellipse } @@ -54,13 +65,6 @@ impl Element for Ellipse { fn atts(&self) -> &[Att] { &self.0 } - - fn to_string(&self) -> String { - format!( - r#""#, - self.0.iter().map(att_to_string).collect::() - ) - } } #[cfg(test)] diff --git a/src/render/svglib/foreign_object.rs b/src/render/svglib/foreign_object.rs index 385a290..98d0bd5 100644 --- a/src/render/svglib/foreign_object.rs +++ b/src/render/svglib/foreign_object.rs @@ -1,5 +1,6 @@ use crate::render::svglib::div::Div; -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, ElementType, SvgElement, Value}; +use std::fmt::Display; pub fn foreign_object() -> ForeignObject { ForeignObject::default() @@ -47,7 +48,21 @@ impl ForeignObject { } } -impl Element for ForeignObject { +impl Display for ForeignObject { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + r#"{}"#, + self.atts.iter().map(att_to_string).collect::(), + self.child + .as_ref() + .map(|c| c.to_string()) + .unwrap_or("".to_string()) + ) + } +} + +impl SvgElement for ForeignObject { fn get_type(&self) -> ElementType { ElementType::ForeignObject } @@ -55,15 +70,4 @@ impl Element for ForeignObject { fn atts(&self) -> &[Att] { &self.atts } - - fn to_string(&self) -> String { - format!( - r#"{}"#, - self.atts.iter().map(att_to_string).collect::(), - self.child - .as_ref() - .map(|c| c.to_string()) - .unwrap_or("".to_string()), - ) - } } diff --git a/src/render/svglib/group.rs b/src/render/svglib/group.rs index ad1b16e..95d05ec 100644 --- a/src/render/svglib/group.rs +++ b/src/render/svglib/group.rs @@ -1,19 +1,18 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, ElementType, SvgElement, Value}; +use std::fmt::Display; pub fn group() -> Group { - Group { - children: Vec::new(), - atts: vec![], - } + Group::default() } +#[derive(Default)] pub struct Group { - children: Vec>, + children: Vec>, atts: Vec, } impl Group { - pub fn add(&mut self, child: impl Element + 'static) { + pub fn add(&mut self, child: impl SvgElement + 'static) { self.children.push(Box::new(child)); } @@ -26,7 +25,21 @@ impl Group { } } -impl Element for Group { +impl Display for Group { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "", + self.atts.iter().map(att_to_string).collect::() + )?; + for e in &self.children { + write!(f, "{}", e.to_string().as_str())?; + } + write!(f, "") + } +} + +impl SvgElement for Group { fn get_type(&self) -> ElementType { ElementType::Group(Vec::new()) } @@ -34,19 +47,6 @@ impl Element for Group { fn atts(&self) -> &[Att] { &self.atts } - - fn to_string(&self) -> String { - let mut svg = format!( - "", - self.atts.iter().map(att_to_string).collect::() - ); - - for e in &self.children { - svg.push_str(e.to_string().as_str()); - } - svg.push_str(""); - svg - } } #[cfg(test)] diff --git a/src/render/svglib/image.rs b/src/render/svglib/image.rs index 56a7d21..55a0608 100644 --- a/src/render/svglib/image.rs +++ b/src/render/svglib/image.rs @@ -1,4 +1,5 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, ElementType, SvgElement, Value}; +use std::fmt::Display; pub fn image() -> Image { Image::default() @@ -39,17 +40,20 @@ impl Image { } } -impl Element for Image { - fn get_type(&self) -> ElementType { - ElementType::Image - } - - fn to_string(&self) -> String { - format!( +impl Display for Image { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, r#""#, self.atts.iter().map(att_to_string).collect::() ) } +} + +impl SvgElement for Image { + fn get_type(&self) -> ElementType { + ElementType::Image + } fn atts(&self) -> &[Att] { &self.atts diff --git a/src/render/svglib/line.rs b/src/render/svglib/line.rs index 3f0ddf1..cae861d 100644 --- a/src/render/svglib/line.rs +++ b/src/render/svglib/line.rs @@ -1,4 +1,5 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, SvgElement, ElementType, Shape, Value}; +use std::fmt::Display; pub fn line() -> Line { Line(vec![]) @@ -34,7 +35,17 @@ impl Line { } } -impl Element for Line { +impl Display for Line { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + r#" "#, + self.0.iter().map(att_to_string).collect::() + ) + } +} + +impl SvgElement for Line { fn get_type(&self) -> ElementType { ElementType::Line } @@ -42,13 +53,6 @@ impl Element for Line { fn atts(&self) -> &[Att] { &self.0 } - - fn to_string(&self) -> String { - format!( - r#" "#, - self.0.iter().map(att_to_string).collect::() - ) - } } impl Shape for Line { diff --git a/src/render/svglib/link.rs b/src/render/svglib/link.rs index 58deb9b..57f3901 100644 --- a/src/render/svglib/link.rs +++ b/src/render/svglib/link.rs @@ -1,4 +1,5 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, SvgElement, ElementType, Value}; +use std::fmt::Display; pub fn link(href: &str) -> Link { Link(vec![att("href", href)]) @@ -12,17 +13,20 @@ impl Link { } } -impl Element for Link { - fn get_type(&self) -> ElementType { - ElementType::Link - } - - fn to_string(&self) -> String { - format!( +impl Display for Link { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, r#""#, self.0.iter().map(att_to_string).collect::() ) } +} + +impl SvgElement for Link { + fn get_type(&self) -> ElementType { + ElementType::Link + } fn atts(&self) -> &[Att] { &self.0 diff --git a/src/render/svglib/mod.rs b/src/render/svglib/mod.rs index 5b2bde2..f3d69c0 100644 --- a/src/render/svglib/mod.rs +++ b/src/render/svglib/mod.rs @@ -76,9 +76,8 @@ pub enum ElementType { Rect, } -pub trait Element { +pub trait SvgElement: Display { fn get_type(&self) -> ElementType; - fn to_string(&self) -> String; fn atts(&self) -> &[Att]; } diff --git a/src/render/svglib/path.rs b/src/render/svglib/path.rs index f078584..21eaee1 100644 --- a/src/render/svglib/path.rs +++ b/src/render/svglib/path.rs @@ -1,4 +1,5 @@ -use crate::render::svglib::{att, Att, Element, ElementType, Shape, Value}; +use crate::render::svglib::{att, att_to_string, Att, ElementType, Shape, SvgElement, Value}; +use std::fmt; pub fn path(d: &str) -> Path { Path::new(d) @@ -9,15 +10,22 @@ pub struct Path { atts: Vec, } -impl Element for Path { +impl fmt::Display for Path { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + r#""#, + self.d, + self.atts.iter().map(att_to_string).collect::() + ) + } +} + +impl SvgElement for Path { fn get_type(&self) -> ElementType { ElementType::Path } - fn to_string(&self) -> String { - todo!() - } - fn atts(&self) -> &[Att] { &self.atts } diff --git a/src/render/svglib/rect.rs b/src/render/svglib/rect.rs index 3263ffe..3638d06 100644 --- a/src/render/svglib/rect.rs +++ b/src/render/svglib/rect.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; +use crate::render::svglib::{att, att_to_string, Att, ElementType, SvgElement, Value}; pub fn rect() -> Rect { Rect::default() @@ -60,7 +60,17 @@ impl Rect { } } -impl Element for Rect { +impl std::fmt::Display for Rect { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + r#""#, + self.atts.iter().map(att_to_string).collect::() + ) + } +} + +impl SvgElement for Rect { fn get_type(&self) -> ElementType { ElementType::Rect } @@ -68,13 +78,6 @@ impl Element for Rect { fn atts(&self) -> &[Att] { &self.atts } - - fn to_string(&self) -> String { - format!( - r#""#, - self.atts.iter().map(att_to_string).collect::() - ) - } } #[cfg(test)] diff --git a/src/render/svglib/svg.rs b/src/render/svglib/svg.rs index 9c53139..54c6456 100644 --- a/src/render/svglib/svg.rs +++ b/src/render/svglib/svg.rs @@ -1,4 +1,4 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, Value}; +use crate::render::svglib::{att, att_to_string, Att, SvgElement, Value}; use std::fmt; pub fn svg() -> Svg { @@ -8,7 +8,7 @@ pub fn svg() -> Svg { #[derive(Default)] pub struct Svg { style: Option, - elements: Vec>, + elements: Vec>, atts: Vec, } @@ -17,7 +17,7 @@ impl Svg { self.style = Some(style.to_string()); } - pub fn add(&mut self, child: impl Element + 'static) { + pub fn add(&mut self, child: impl SvgElement + 'static) { self.elements.push(Box::new(child)); } @@ -35,7 +35,7 @@ impl Svg { pub fn preserveaspectratio>(&mut self, preserveaspectratio: V) { self.atts - .push(att("preserveaspectratio", preserveaspectratio)); + .push(att("preserveAspectRatio", preserveaspectratio)); } pub fn transform>(&mut self, transform: V) { @@ -47,7 +47,7 @@ impl fmt::Display for Svg { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, - r#"{}"#, + r#"{}"#, self.atts.iter().map(att_to_string).collect::(), self.style .as_ref() @@ -83,7 +83,7 @@ mod tests { svg.preserveaspectratio("none"); svg.add(rect().x(0).y(0).width(10).height(10)); assert_eq!( - r#""#, + r#""#, svg.to_string() ) } diff --git a/src/render/svglib/text.rs b/src/render/svglib/text.rs index c968dd5..fc90317 100644 --- a/src/render/svglib/text.rs +++ b/src/render/svglib/text.rs @@ -1,5 +1,5 @@ -use crate::render::svglib::{att, att_to_string, Att, Element, ElementType, Value}; -use std::fmt::Write; +use crate::render::svglib::{att, att_to_string, Att, ElementType, SvgElement, Value}; +use std::fmt::{Display, Write}; pub fn text() -> Text { Text::default() @@ -74,16 +74,11 @@ impl Text { } } -impl Element for Text { - fn get_type(&self) -> ElementType { - ElementType::Rect - } - - fn to_string(&self) -> String { - // let x: Vec<&Att> = self.atts.iter().filter(|a| a.name == "x").collect(); - // let x: String = x[0].value.to_string(); - format!( - r#"{}"#, +impl Display for Text { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + r#""#, self.atts.iter().map(att_to_string).collect::(), self.text.lines().fold(String::new(), |mut output, l| { let x: Vec<&Att> = self.atts.iter().filter(|a| a.name == "x").collect(); @@ -93,6 +88,12 @@ impl Element for Text { }) ) } +} + +impl SvgElement for Text { + fn get_type(&self) -> ElementType { + ElementType::Rect + } fn atts(&self) -> &[Att] { &self.atts @@ -105,10 +106,10 @@ mod tests { #[test] fn test_rect() { - let rect = text().x(0).y(0).width(10).height(10); + let text = text().x(0).y(0).width(10).height(10); assert_eq!( - r#""#, - rect.to_string() + r#""#, + text.to_string() ) } }