mt_dom/node/
attribute.rs

1#![allow(clippy::type_complexity)]
2use alloc::vec;
3use alloc::vec::Vec;
4use core::fmt::Debug;
5
6/// These are the plain attributes of an element
7#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct Attribute<Ns, Att, Val>
9where
10    Ns: PartialEq + Clone + Debug,
11    Att: PartialEq + Clone + Debug,
12    Val: PartialEq + Clone + Debug,
13{
14    /// namespace of an attribute.
15    /// This is specifically used by svg attributes
16    /// such as xlink-href
17    pub namespace: Option<Ns>,
18    /// the attribute name,
19    /// optional since style attribute doesn't need to have an attribute name
20    pub name: Att,
21    /// the attribute value, which could be a simple value, and event or a function call
22    pub value: Vec<Val>,
23}
24
25impl<Ns, Att, Val> Attribute<Ns, Att, Val>
26where
27    Ns: PartialEq + Clone + Debug,
28    Att: PartialEq + Clone + Debug,
29    Val: PartialEq + Clone + Debug,
30{
31    /// create a plain attribute with namespace
32    pub fn new(namespace: Option<Ns>, name: Att, value: Val) -> Self {
33        Attribute {
34            name,
35            value: vec![value],
36            namespace,
37        }
38    }
39
40    /// create from multiple values
41    pub fn with_multiple_values(
42        namespace: Option<Ns>,
43        name: Att,
44        value: impl IntoIterator<Item = Val>,
45    ) -> Self {
46        Attribute {
47            name,
48            value: value.into_iter().collect(),
49            namespace,
50        }
51    }
52
53    /// return the name of this attribute
54    pub fn name(&self) -> &Att {
55        &self.name
56    }
57
58    /// return the value of this attribute
59    pub fn value(&self) -> &[Val] {
60        &self.value
61    }
62
63    /// return the namespace of this attribute
64    pub fn namespace(&self) -> Option<&Ns> {
65        self.namespace.as_ref()
66    }
67}
68
69/// Create an attribute
70/// # Example
71/// ```rust
72/// use mt_dom::{Attribute,attr};
73/// let class: Attribute<&'static str, &'static str, &'static str> =
74///     attr("class", "container");
75/// ```
76#[inline]
77pub fn attr<Ns, Att, Val>(name: Att, value: Val) -> Attribute<Ns, Att, Val>
78where
79    Ns: PartialEq + Clone + Debug,
80    Att: PartialEq + Clone + Debug,
81    Val: PartialEq + Clone + Debug,
82{
83    attr_ns(None, name, value)
84}
85
86/// Create an attribute with namespace
87/// # Example
88/// ```rust
89/// use mt_dom::{Attribute,attr_ns};
90///
91/// let href: Attribute<&'static str, &'static str, &'static str> =
92///     attr_ns(Some("http://www.w3.org/1999/xlink"), "href", "cool-script.js");
93/// ```
94#[inline]
95pub fn attr_ns<Ns, Att, Val>(
96    namespace: Option<Ns>,
97    name: Att,
98    value: Val,
99) -> Attribute<Ns, Att, Val>
100where
101    Ns: PartialEq + Clone + Debug,
102    Att: PartialEq + Clone + Debug,
103    Val: PartialEq + Clone + Debug,
104{
105    Attribute::new(namespace, name, value)
106}
107
108/// merge the values of attributes with the same name
109#[doc(hidden)]
110pub fn merge_attributes_of_same_name<Ns, Att, Val>(
111    attributes: &[&Attribute<Ns, Att, Val>],
112) -> Vec<Attribute<Ns, Att, Val>>
113where
114    Ns: PartialEq + Clone + Debug,
115    Att: PartialEq + Clone + Debug,
116    Val: PartialEq + Clone + Debug,
117{
118    let mut merged: Vec<Attribute<Ns, Att, Val>> = vec![];
119    for att in attributes {
120        if let Some(existing) =
121            merged.iter_mut().find(|m_att| m_att.name == att.name)
122        {
123            existing.value.extend(att.value.clone());
124        } else {
125            merged.push(Attribute {
126                namespace: None,
127                name: att.name.clone(),
128                value: att.value.clone(),
129            });
130        }
131    }
132    merged
133}
134
135/// group attributes of the same name
136#[doc(hidden)]
137pub fn group_attributes_per_name<Ns, Att, Val>(
138    attributes: &[Attribute<Ns, Att, Val>],
139) -> Vec<(&Att, Vec<&Attribute<Ns, Att, Val>>)>
140where
141    Ns: PartialEq + Clone + Debug,
142    Att: PartialEq + Clone + Debug,
143    Val: PartialEq + Clone + Debug,
144{
145    let mut grouped: Vec<(&Att, Vec<&Attribute<Ns, Att, Val>>)> = vec![];
146    for attr in attributes {
147        if let Some(existing) = grouped
148            .iter_mut()
149            .find(|(g_att, _)| **g_att == attr.name)
150            .map(|(_, attr)| attr)
151        {
152            existing.push(attr);
153        } else {
154            grouped.push((&attr.name, vec![attr]))
155        }
156    }
157    grouped
158}