1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#![allow(clippy::type_complexity)]
use alloc::vec;
use alloc::vec::Vec;
use core::fmt::Debug;

/// These are the plain attributes of an element
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute<Ns, Att, Val>
where
    Ns: PartialEq + Clone + Debug,
    Att: PartialEq + Clone + Debug,
    Val: PartialEq + Clone + Debug,
{
    /// namespace of an attribute.
    /// This is specifically used by svg attributes
    /// such as xlink-href
    pub namespace: Option<Ns>,
    /// the attribute name,
    /// optional since style attribute doesn't need to have an attribute name
    pub name: Att,
    /// the attribute value, which could be a simple value, and event or a function call
    pub value: Vec<Val>,
}

impl<Ns, Att, Val> Attribute<Ns, Att, Val>
where
    Ns: PartialEq + Clone + Debug,
    Att: PartialEq + Clone + Debug,
    Val: PartialEq + Clone + Debug,
{
    /// create a plain attribute with namespace
    pub fn new(namespace: Option<Ns>, name: Att, value: Val) -> Self {
        Attribute {
            name,
            value: vec![value],
            namespace,
        }
    }

    /// create from multiple values
    pub fn with_multiple_values(
        namespace: Option<Ns>,
        name: Att,
        value: impl IntoIterator<Item = Val>,
    ) -> Self {
        Attribute {
            name,
            value: value.into_iter().collect(),
            namespace,
        }
    }

    /// return the name of this attribute
    pub fn name(&self) -> &Att {
        &self.name
    }

    /// return the value of this attribute
    pub fn value(&self) -> &[Val] {
        &self.value
    }

    /// return the namespace of this attribute
    pub fn namespace(&self) -> Option<&Ns> {
        self.namespace.as_ref()
    }
}

/// Create an attribute
/// # Example
/// ```rust
/// use mt_dom::{Attribute,attr};
/// let class: Attribute<&'static str, &'static str, &'static str> =
///     attr("class", "container");
/// ```
#[inline]
pub fn attr<Ns, Att, Val>(name: Att, value: Val) -> Attribute<Ns, Att, Val>
where
    Ns: PartialEq + Clone + Debug,
    Att: PartialEq + Clone + Debug,
    Val: PartialEq + Clone + Debug,
{
    attr_ns(None, name, value)
}

/// Create an attribute with namespace
/// # Example
/// ```rust
/// use mt_dom::{Attribute,attr_ns};
///
/// let href: Attribute<&'static str, &'static str, &'static str> =
///     attr_ns(Some("http://www.w3.org/1999/xlink"), "href", "cool-script.js");
/// ```
#[inline]
pub fn attr_ns<Ns, Att, Val>(
    namespace: Option<Ns>,
    name: Att,
    value: Val,
) -> Attribute<Ns, Att, Val>
where
    Ns: PartialEq + Clone + Debug,
    Att: PartialEq + Clone + Debug,
    Val: PartialEq + Clone + Debug,
{
    Attribute::new(namespace, name, value)
}

/// merge the values of attributes with the same name
#[doc(hidden)]
pub fn merge_attributes_of_same_name<Ns, Att, Val>(
    attributes: &[&Attribute<Ns, Att, Val>],
) -> Vec<Attribute<Ns, Att, Val>>
where
    Ns: PartialEq + Clone + Debug,
    Att: PartialEq + Clone + Debug,
    Val: PartialEq + Clone + Debug,
{
    let mut merged: Vec<Attribute<Ns, Att, Val>> = vec![];
    for att in attributes {
        if let Some(existing) =
            merged.iter_mut().find(|m_att| m_att.name == att.name)
        {
            existing.value.extend(att.value.clone());
        } else {
            merged.push(Attribute {
                namespace: None,
                name: att.name.clone(),
                value: att.value.clone(),
            });
        }
    }
    merged
}

/// group attributes of the same name
#[doc(hidden)]
pub fn group_attributes_per_name<Ns, Att, Val>(
    attributes: &[Attribute<Ns, Att, Val>],
) -> Vec<(&Att, Vec<&Attribute<Ns, Att, Val>>)>
where
    Ns: PartialEq + Clone + Debug,
    Att: PartialEq + Clone + Debug,
    Val: PartialEq + Clone + Debug,
{
    let mut grouped: Vec<(&Att, Vec<&Attribute<Ns, Att, Val>>)> = vec![];
    for attr in attributes {
        if let Some(existing) = grouped
            .iter_mut()
            .find(|(g_att, _)| **g_att == attr.name)
            .map(|(_, attr)| attr)
        {
            existing.push(attr);
        } else {
            grouped.push((&attr.name, vec![attr]))
        }
    }
    grouped
}