svg/node/element/
mod.rs

1//! The element nodes.
2
3#![allow(clippy::new_without_default)]
4#![allow(clippy::should_implement_trait)]
5
6use std::collections::hash_map::DefaultHasher;
7use std::fmt;
8use std::hash::Hash;
9
10use crate::node::{Attributes, Children, Node, Value};
11
12pub mod path;
13pub mod tag;
14
15/// An element.
16#[derive(Clone, Debug)]
17pub struct Element {
18    name: String,
19    attributes: Attributes,
20    children: Children,
21}
22
23impl Element {
24    /// Create an element.
25    pub fn new<T>(name: T) -> Self
26    where
27        T: Into<String>,
28    {
29        Element {
30            name: name.into(),
31            attributes: Attributes::new(),
32            children: Children::new(),
33        }
34    }
35
36    /// Return the name.
37    #[inline]
38    pub fn get_name(&self) -> &str {
39        &self.name
40    }
41
42    /// Return the attributes.
43    #[inline]
44    pub fn get_attributes(&self) -> &Attributes {
45        &self.attributes
46    }
47
48    /// Return the attributes as mutable.
49    #[inline]
50    pub fn get_attributes_mut(&mut self) -> &mut Attributes {
51        &mut self.attributes
52    }
53
54    /// Return the children.
55    #[inline]
56    pub fn get_children(&self) -> &Children {
57        &self.children
58    }
59
60    /// Return the children as mutable.
61    #[inline]
62    pub fn get_children_mut(&mut self) -> &mut Children {
63        &mut self.children
64    }
65}
66
67impl fmt::Display for Element {
68    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
69        write!(formatter, "<{}", self.name)?;
70        let mut attributes = self.attributes.iter().collect::<Vec<_>>();
71        attributes.sort_by_key(|pair| pair.0.as_str());
72        for (name, value) in attributes {
73            write!(formatter, r#" {}="{}""#, name, escape(value))?;
74        }
75        if self.children.is_empty() {
76            return write!(formatter, "/>");
77        }
78        write!(formatter, ">")?;
79        let mut bare = false;
80        for child in self.children.iter() {
81            bare = child.is_bare() && !formatter.alternate();
82            if !bare {
83                writeln!(formatter)?;
84            }
85            write!(formatter, "{}", child)?;
86        }
87        if !bare {
88            writeln!(formatter)?;
89        }
90        write!(formatter, "</{}>", self.name)
91    }
92}
93
94impl Node for Element {
95    #[inline]
96    fn append<T>(&mut self, node: T)
97    where
98        T: Into<Box<dyn Node>>,
99    {
100        self.children.push(node.into());
101    }
102
103    #[inline]
104    fn assign<T, U>(&mut self, name: T, value: U)
105    where
106        T: Into<String>,
107        U: Into<Value>,
108    {
109        self.attributes.insert(name.into(), value.into());
110    }
111
112    #[inline]
113    fn get_name(&self) -> &str {
114        Self::get_name(self)
115    }
116
117    #[inline]
118    fn get_attributes(&self) -> Option<&Attributes> {
119        Self::get_attributes(self).into()
120    }
121
122    #[inline]
123    fn get_attributes_mut(&mut self) -> Option<&mut Attributes> {
124        Self::get_attributes_mut(self).into()
125    }
126
127    #[inline]
128    fn get_children(&self) -> Option<&Children> {
129        Self::get_children(self).into()
130    }
131
132    #[inline]
133    fn get_children_mut(&mut self) -> Option<&mut Children> {
134        Self::get_children_mut(self).into()
135    }
136}
137
138macro_rules! implement_nested(
139    ($struct_name:ident::$field_name:ident) => (
140        implement_nested!($struct_name::$field_name []);
141    );
142    ($struct_name:ident::$field_name:ident [$($indicator_name:ident),*]) => (
143        impl $struct_name {
144            /// Append a node.
145            pub fn add<T>(mut self, node: T) -> Self
146            where
147                T: Into<Box<dyn Node>>,
148            {
149                Node::append(&mut self, node);
150                self
151            }
152
153            /// Assign an attribute.
154            #[inline]
155            pub fn set<T, U>(mut self, name: T, value: U) -> Self
156            where
157                T: Into<String>,
158                U: Into<Value>,
159            {
160                Node::assign(&mut self, name, value);
161                self
162            }
163        }
164
165        impl Node for $struct_name {
166            #[inline]
167            fn append<T>(&mut self, node: T)
168            where
169                T: Into<Box<dyn Node>>,
170            {
171                self.$field_name.append(node);
172            }
173
174            #[inline]
175            fn assign<T, U>(&mut self, name: T, value: U)
176            where
177                T: Into<String>,
178                U: Into<Value>,
179            {
180                self.$field_name.assign(name, value);
181            }
182
183            #[inline]
184            fn get_name(&self) -> &str {
185                self.$field_name.get_name()
186            }
187
188            #[inline]
189            fn get_attributes(&self) -> Option<&Attributes> {
190                self.$field_name.get_attributes().into()
191            }
192
193            #[inline]
194            fn get_attributes_mut(&mut self) -> Option<&mut Attributes> {
195                self.$field_name.get_attributes_mut().into()
196            }
197
198            #[inline]
199            fn get_children(&self) -> Option<&Children> {
200                self.$field_name.get_children().into()
201            }
202
203            #[inline]
204            fn get_children_mut(&mut self) -> Option<&mut Children> {
205                self.$field_name.get_children_mut().into()
206            }
207
208            $(
209                #[inline]
210                fn $indicator_name(&self) -> bool {
211                    true
212                }
213            )*
214        }
215
216        impl std::ops::Deref for $struct_name {
217            type Target = Element;
218
219            #[inline]
220            fn deref(&self) -> &Self::Target {
221                &self.$field_name
222            }
223        }
224
225        impl std::ops::DerefMut for $struct_name {
226            #[inline]
227            fn deref_mut(&mut self) -> &mut Self::Target {
228                &mut self.$field_name
229            }
230        }
231
232        impl std::fmt::Display for $struct_name {
233            #[inline]
234            fn fmt(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
235                if self.is_bareable() {
236                    write!(formatter, "{:#}", self.$field_name)
237                } else {
238                    self.$field_name.fmt(formatter)
239                }
240            }
241        }
242
243        impl From<$struct_name> for Element {
244            #[inline]
245            fn from(value: $struct_name) -> Self {
246                value.$field_name
247            }
248        }
249    );
250);
251
252macro_rules! implement {
253    ($(#[$doc:meta] struct $struct_name:ident)*) => ($(
254        #[$doc]
255        #[derive(Clone, Debug)]
256        pub struct $struct_name {
257            inner: Element,
258        }
259
260        impl $struct_name {
261            /// Create a node.
262            #[inline]
263            pub fn new() -> Self {
264                $struct_name {
265                    inner: Element::new(tag::$struct_name),
266                }
267            }
268        }
269
270        impl Default for $struct_name {
271            fn default() -> Self {
272                Self::new()
273            }
274        }
275
276        impl super::NodeDefaultHash for $struct_name {
277            #[inline]
278            fn default_hash(&self, state: &mut DefaultHasher) {
279                self.inner.default_hash(state);
280            }
281        }
282
283        implement_nested! { $struct_name::inner }
284    )*);
285}
286
287impl super::NodeDefaultHash for Element {
288    fn default_hash(&self, state: &mut DefaultHasher) {
289        self.name.hash(state);
290        self.attributes.iter().for_each(|(key, value)| {
291            key.hash(state);
292            value.hash(state)
293        });
294        self.children
295            .iter()
296            .for_each(|child| child.default_hash(state));
297    }
298}
299
300implement! {
301    #[doc = "An [`a`](https://www.w3.org/TR/SVG/linking.html#AElement) element."]
302    struct Anchor
303
304    #[doc = "An [`animate`](https://www.w3.org/TR/SVG/animate.html#AnimateElement) element."]
305    struct Animate
306
307    #[doc = "An [`animateColor`](https://www.w3.org/TR/SVG/animate.html#AnimateColorElement) element."]
308    struct AnimateColor
309
310    #[doc = "An [`animateMotion`](https://www.w3.org/TR/SVG/animate.html#AnimateMotionElement) element."]
311    struct AnimateMotion
312
313    #[doc = "An [`animateTransform`](https://www.w3.org/TR/SVG/animate.html#AnimateTransformElement) element."]
314    struct AnimateTransform
315
316    #[doc = "A [`circle`](https://www.w3.org/TR/SVG/shapes.html#CircleElement) element."]
317    struct Circle
318
319    #[doc = "A [`clipPath`](https://www.w3.org/TR/SVG/masking.html#ClipPathElement) element."]
320    struct ClipPath
321
322    #[doc = "A [`defs`](https://www.w3.org/TR/SVG/struct.html#DefsElement) element."]
323    struct Definitions
324
325    #[doc = "A [`desc`](https://www.w3.org/TR/SVG/struct.html#DescElement) element."]
326    struct Description
327
328    #[doc = "An [`ellipse`](https://www.w3.org/TR/SVG/shapes.html#EllipseElement) element."]
329    struct Ellipse
330
331    #[doc = "A [`filter`](https://www.w3.org/TR/SVG/filters.html#FilterElement) element."]
332    struct Filter
333
334    #[doc = "A [`feBlend`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feBlend) element."]
335    struct FilterEffectBlend
336
337    #[doc = "A [`feColorMatrix`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feColorMatrix) element."]
338    struct FilterEffectColorMatrix
339
340    #[doc = "A [`feComponentTransfer`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComponentTransfer) element."]
341    struct FilterEffectComponentTransfer
342
343    #[doc = "A [`feComposite`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feComposite) element."]
344    struct FilterEffectComposite
345
346    #[doc = "A [`feConvolveMatrix`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feConvolveMatrix) element."]
347    struct FilterEffectConvolveMatrix
348
349    #[doc = "A [`feDiffuseLighting`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDiffuseLighting) element."]
350    struct FilterEffectDiffuseLighting
351
352    #[doc = "A [`feDisplacementMap`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDisplacementMap) element."]
353    struct FilterEffectDisplacementMap
354
355    #[doc = "A [`feDistantLight`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDistantLight) element."]
356    struct FilterEffectDistantLight
357
358    #[doc = "A [`feDropShadow`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDropShadow) element."]
359    struct FilterEffectDropShadow
360
361    #[doc = "A [`feFlood`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFlood) element."]
362    struct FilterEffectFlood
363
364    #[doc = "A [`feFuncA`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncA) element."]
365    struct FilterEffectFunctionA
366
367    #[doc = "A [`feFuncB`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncB) element."]
368    struct FilterEffectFunctionB
369
370    #[doc = "A [`feFuncG`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncG) element."]
371    struct FilterEffectFunctionG
372
373    #[doc = "A [`feFuncR`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feFuncR) element."]
374    struct FilterEffectFunctionR
375
376    #[doc = "A [`feGaussianBlur`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feGaussianBlur) element."]
377    struct FilterEffectGaussianBlur
378
379    #[doc = "A [`feImage`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feImage) element."]
380    struct FilterEffectImage
381
382    #[doc = "A [`feMerge`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feMerge) element."]
383    struct FilterEffectMerge
384
385    #[doc = "A [`feMergeNode`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feMergeNode) element."]
386    struct FilterEffectMergeNode
387
388    #[doc = "A [`feMorphology`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feMorphology) element."]
389    struct FilterEffectMorphology
390
391    #[doc = "A [`feOffset`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feOffset) element."]
392    struct FilterEffectOffset
393
394    #[doc = "A [`fePointLight`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/fePointLight) element."]
395    struct FilterEffectPointLight
396
397    #[doc = "A [`feSpecularLighting`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feSpecularLighting) element."]
398    struct FilterEffectSpecularLighting
399
400    #[doc = "A [`feSpotLight`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feSpotLight) element."]
401    struct FilterEffectSpotLight
402
403    #[doc = "A [`feTile`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feTile) element."]
404    struct FilterEffectTile
405
406    #[doc = "A [`feTurbulence`](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feTurbulence) element."]
407    struct FilterEffectTurbulence
408
409    #[doc = "A [`foreignObject`](https://www.w3.org/TR/SVG/embedded.html#ForeignObjectElement) element."]
410    struct ForeignObject
411
412    #[doc = "A [`g`](https://www.w3.org/TR/SVG/struct.html#GElement) element."]
413    struct Group
414
415    #[doc = "An [`image`](https://www.w3.org/TR/SVG/struct.html#ImageElement) element."]
416    struct Image
417
418    #[doc = "A [`line`](https://www.w3.org/TR/SVG/shapes.html#LineElement) element."]
419    struct Line
420
421    #[doc = "A [`linearGradient`](https://www.w3.org/TR/SVG/pservers.html#LinearGradientElement) element."]
422    struct LinearGradient
423
424    #[doc = "A [`link`](https://www.w3.org/TR/SVG/styling.html#LinkElement) element."]
425    struct Link
426
427    #[doc = "A [`marker`](https://www.w3.org/TR/SVG/painting.html#MarkerElement) element."]
428    struct Marker
429
430    #[doc = "A [`mask`](https://www.w3.org/TR/SVG/masking.html#MaskElement) element."]
431    struct Mask
432
433    #[doc = "An [`mpath`](https://www.w3.org/TR/SVG/animate.html#MPathElement) element."]
434    struct MotionPath
435
436    #[doc = "A [`path`](https://www.w3.org/TR/SVG/paths.html#PathElement) element."]
437    struct Path
438
439    #[doc = "A [`pattern`](https://www.w3.org/TR/SVG/pservers.html#PatternElement) element."]
440    struct Pattern
441
442    #[doc = "A [`polygon`](https://www.w3.org/TR/SVG/shapes.html#PolygonElement) element."]
443    struct Polygon
444
445    #[doc = "A [`polyline`](https://www.w3.org/TR/SVG/shapes.html#PolylineElement) element."]
446    struct Polyline
447
448    #[doc = "A [`radialGradient`](https://www.w3.org/TR/SVG/pservers.html#RadialGradientElement) element."]
449    struct RadialGradient
450
451    #[doc = "A [`rect`](https://www.w3.org/TR/SVG/shapes.html#RectElement) element."]
452    struct Rectangle
453
454    #[doc = "A [`stop`](https://www.w3.org/TR/SVG/pservers.html#StopElement) element."]
455    struct Stop
456
457    #[doc = "A [`symbol`](https://www.w3.org/TR/SVG/struct.html#SymbolElement) element."]
458    struct Symbol
459
460    #[doc = "A [`use`](https://www.w3.org/TR/SVG/struct.html#UseElement) element."]
461    struct Use
462}
463
464macro_rules! implement {
465    (@itemize $i:item) => ($i);
466    ($(
467        #[$doc:meta]
468        struct $struct_name:ident
469        [$($indicator_name:ident),*]
470        [$($trait_name:ident: $($trait_type:tt)*),*]
471        [$inner:ident $(,$argument_name:ident: $argument_type:ty)*] $body:block
472    )*) => ($(
473        #[$doc]
474        #[derive(Clone, Debug)]
475        pub struct $struct_name {
476            inner: Element,
477        }
478
479        implement! { @itemize
480            impl $struct_name {
481                /// Create a node.
482                #[inline]
483                pub fn new<$($trait_name: $($trait_type)*),*>($($argument_name: $argument_type),*) -> Self {
484                    #[inline(always)]
485                    fn initialize<$($trait_name: $($trait_type)*),*>(
486                        $inner: &mut Element $(, $argument_name: $argument_type)*
487                    ) $body
488                    let mut inner = Element::new(tag::$struct_name);
489                    initialize(&mut inner $(, $argument_name)*);
490                    $struct_name {
491                        inner,
492                    }
493                }
494            }
495        }
496
497        impl super::NodeDefaultHash for $struct_name {
498            fn default_hash(&self, state: &mut DefaultHasher) {
499                self.inner.default_hash(state);
500            }
501        }
502
503        implement_nested! { $struct_name::inner [$($indicator_name),*] }
504    )*);
505}
506
507implement! {
508    #[doc = "An [`svg`](https://www.w3.org/TR/SVG/struct.html#SVGElement) element."]
509    struct SVG [is_bareable] [] [inner] {
510        inner.assign("xmlns", "http://www.w3.org/2000/svg");
511    }
512
513    #[doc = "A [`script`](https://www.w3.org/TR/SVG/script.html#ScriptElement) element."]
514    struct Script [is_bareable] [T: Into<String>] [inner, content: T] {
515        inner.append(crate::node::Text::new(content));
516    }
517
518    #[doc = "A [`style`](https://www.w3.org/TR/SVG/styling.html#StyleElement) element."]
519    struct Style [is_bareable] [T: Into<String>] [inner, content: T] {
520        inner.append(crate::node::Text::new(content));
521    }
522
523    #[doc = "A [`text`](https://www.w3.org/TR/SVG/text.html#TextElement) element."]
524    struct Text [is_bareable] [T: Into<String>] [inner, content: T] {
525        inner.append(crate::node::Text::new(content));
526    }
527
528    #[doc = "A [`textPath`](https://www.w3.org/TR/SVG/text.html#TextPathElement) element."]
529    struct TextPath [] [T: Into<String>] [inner, content: T] {
530        inner.append(crate::node::Text::new(content));
531    }
532
533    #[doc = "A [`title`](https://www.w3.org/TR/SVG/struct.html#TitleElement) element."]
534    struct Title [] [T: Into<String>] [inner, content: T] {
535        inner.append(crate::node::Text::new(content));
536    }
537
538    #[doc = "A [`tspan`](https://www.w3.org/TR/SVG/text.html#TextElement) element."]
539    struct TSpan [] [T: Into<String>] [inner, content: T] {
540        inner.append(crate::node::Text::new(content));
541    }
542}
543
544fn escape(value: &str) -> String {
545    crate::node::text::escape(value)
546        .replace('"', "&quot;")
547        .replace('\'', "&apos;")
548}
549
550#[cfg(test)]
551mod tests {
552    use super::{Element, Rectangle, Style, Title};
553    use crate::node::element;
554
555    #[test]
556    fn element_children() {
557        let mut one = element::Group::new()
558            .add(element::Text::new("foo"))
559            .add(element::Text::new("bar"))
560            .add(element::Text::new("buz"));
561        let two = element::Group::new()
562            .add(one.get_children()[0].clone())
563            .add(one.get_children_mut().pop().unwrap());
564
565        assert_eq!(
566            one.to_string(),
567            "<g>\n<text>\nfoo\n</text>\n<text>\nbar\n</text>\n</g>",
568        );
569        assert_eq!(
570            two.to_string(),
571            "<g>\n<text>\nfoo\n</text>\n<text>\nbuz\n</text>\n</g>",
572        );
573    }
574
575    #[test]
576    fn element_display() {
577        use crate::node::Node;
578
579        let mut element = Element::new("foo");
580        element.assign("x", -10);
581        element.assign("y", "10px");
582        element.assign("s", (12.5, 13.0));
583        element.assign("c", "green");
584        element.append(Element::new("bar"));
585
586        assert_eq!(
587            element.to_string().lines().collect::<Vec<_>>(),
588            &[
589                r#"<foo c="green" s="12.5 13" x="-10" y="10px">"#,
590                "<bar/>",
591                "</foo>",
592            ],
593        );
594    }
595
596    #[test]
597    fn element_display_angles() {
598        let element = Rectangle::new()
599            .set("fill", "#FF780088")
600            .set("height", 10)
601            .set("width", 0.3088995)
602            .set("x", 328.0725)
603            .set("y", 120)
604            .add(Title::new("widgets >=3.0.9, <3.1.dev0"));
605
606        assert_eq!(
607            element.to_string().lines().collect::<Vec<_>>(),
608            &[
609                r###"<rect fill="#FF780088" height="10" width="0.3088995" x="328.0725" y="120">"###,
610                "<title>widgets &gt;=3.0.9, &lt;3.1.dev0</title>",
611                "</rect>",
612            ],
613        );
614    }
615
616    #[test]
617    fn element_display_quotes() {
618        use crate::node::Node;
619
620        let mut element = Element::new("foo");
621        element.assign("s", "'single'");
622        element.assign("d", r#""double""#);
623        element.assign("m", r#""mixed'"#);
624
625        assert_eq!(
626            element.to_string(),
627            r#"<foo d="&quot;double&quot;" m="&quot;mixed&apos;" s="&apos;single&apos;"/>"#,
628        );
629    }
630
631    #[test]
632    fn style_display() {
633        let element = Style::new("* { font-family: foo; }");
634
635        assert_eq!(
636            element.to_string().lines().collect::<Vec<_>>(),
637            &["<style>", "* { font-family: foo; }", "</style>"],
638        );
639    }
640}