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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
pub use attribute::Attribute;
use attribute::Callback;
pub use element::Element;

pub(crate) mod attribute;
mod element;

/// represents a node in a virtual dom
/// A node could be an element which can contain one or more children of nodes.
/// A node could also be just a text node which contains a string
///
/// Much of the types are Generics
///
/// NS - is the type for the namespace, this will be &'static str when used in html based virtual dom implementation
/// TAG - is the type for the element tag, this will be &'static str when used in html based virtual
/// dom impmenentation
/// ATT - is the type for the attribute name, this will be &'static str when used in html based
/// virtual dom implementation
/// VAL - is the type for the value of the attribute, this will be String, f64, or just another
/// generics that suits the implementing library which used mt-dom for just dom-diffing purposes
#[derive(Debug, Clone, PartialEq)]
pub enum Node<NS, TAG, ATT, VAL, EVENT, MSG> {
    /// Element variant of a virtual node
    Element(Element<NS, TAG, ATT, VAL, EVENT, MSG>),
    /// Text variant of a virtual node
    Text(String),
}

impl<NS, TAG, ATT, VAL, EVENT, MSG> Node<NS, TAG, ATT, VAL, EVENT, MSG> {
    /// returns true if this a text node
    pub fn is_text(&self) -> bool {
        match self {
            Node::Element(_) => false,
            Node::Text(_) => true,
        }
    }

    /// consume self and return the element if it is an element variant
    /// None if it is a text node
    pub fn take_element(self) -> Option<Element<NS, TAG, ATT, VAL, EVENT, MSG>> {
        match self {
            Node::Element(element) => Some(element),
            Node::Text(_) => None,
        }
    }

    /// Get a mutable reference to the element, if this node is an element node
    pub fn as_element_mut(&mut self) -> Option<&mut Element<NS, TAG, ATT, VAL, EVENT, MSG>> {
        match *self {
            Node::Element(ref mut element) => Some(element),
            Node::Text(_) => None,
        }
    }

    /// returns a reference to the element if this is an element node
    pub fn as_element_ref(&self) -> Option<&Element<NS, TAG, ATT, VAL, EVENT, MSG>> {
        match *self {
            Node::Element(ref element) => Some(element),
            Node::Text(_) => None,
        }
    }

    /// Consume a mutable self and add a children to this node it if is an element
    /// will have no effect if it is a text node.
    /// This is used in building the nodes in a builder pattern
    pub fn add_children(mut self, children: Vec<Node<NS, TAG, ATT, VAL, EVENT, MSG>>) -> Self {
        if let Some(element) = self.as_element_mut() {
            element.add_children(children);
        }
        self
    }

    /// add attributes to the node and returns itself
    /// this is used in view building
    pub fn add_attributes(mut self, attributes: Vec<Attribute<NS, ATT, VAL, EVENT, MSG>>) -> Self {
        if let Some(elm) = self.as_element_mut() {
            elm.add_attributes(attributes);
        }
        self
    }

    /// add attributes using a mutable reference to self
    pub fn add_attributes_ref_mut(&mut self, attributes: Vec<Attribute<NS, ATT, VAL, EVENT, MSG>>) {
        if let Some(elm) = self.as_element_mut() {
            elm.add_attributes(attributes);
        }
    }

    /// get the attributes of this node
    /// returns None if it is a text node
    pub fn get_attributes(&self) -> Option<&[Attribute<NS, ATT, VAL, EVENT, MSG>]> {
        match *self {
            Node::Element(ref element) => Some(element.get_attributes()),
            Node::Text(_) => None,
        }
    }

    /// returns the tag of this node if it is an element
    /// otherwise None if it is a text node
    pub fn tag(&self) -> Option<&TAG> {
        if let Some(e) = self.as_element_ref() {
            Some(&e.tag)
        } else {
            None
        }
    }

    /// returns the text content if it is a text node
    pub fn text(&self) -> Option<&str> {
        match self {
            Node::Text(t) => Some(&t),
            Node::Element(_) => None,
        }
    }

    /// return the children of this node if it is an element
    /// returns None if it is a text node
    pub fn get_children(&self) -> Option<&[Node<NS, TAG, ATT, VAL, EVENT, MSG>]> {
        if let Some(element) = self.as_element_ref() {
            Some(element.get_children())
        } else {
            None
        }
    }
}

impl<NS, TAG, ATT, VAL, EVENT, MSG> Node<NS, TAG, ATT, VAL, EVENT, MSG>
where
    ATT: PartialEq,
{
    /// remove the existing attributes and set with the new value
    pub fn set_attributes_ref_mut(&mut self, attributes: Vec<Attribute<NS, ATT, VAL, EVENT, MSG>>) {
        if let Some(elm) = self.as_element_mut() {
            elm.set_attributes(attributes);
        }
    }
}

impl<NS, TAG, ATT, VAL, EVENT, MSG> Node<NS, TAG, ATT, VAL, EVENT, MSG>
where
    EVENT: 'static,
    MSG: 'static,
{
    /// map the msg of callback of this element node
    pub fn map_msg<F, MSG2>(self, func: F) -> Node<NS, TAG, ATT, VAL, EVENT, MSG2>
    where
        F: Fn(MSG) -> MSG2 + 'static,
        MSG2: 'static,
    {
        let cb = Callback::from(func);
        self.map_callback(cb)
    }

    /// map the msg of callback of this element node
    pub fn map_callback<MSG2>(self, cb: Callback<MSG, MSG2>) -> Node<NS, TAG, ATT, VAL, EVENT, MSG2>
    where
        MSG2: 'static,
    {
        match self {
            Node::Element(element) => Node::Element(element.map_callback(cb)),
            Node::Text(text) => Node::Text(text),
        }
    }
}

/// create a virtual node with tag, attrs and children
#[inline]
pub fn element<NS, TAG, ATT, VAL, EVENT, MSG>(
    tag: TAG,
    attrs: Vec<Attribute<NS, ATT, VAL, EVENT, MSG>>,
    children: Vec<Node<NS, TAG, ATT, VAL, EVENT, MSG>>,
) -> Node<NS, TAG, ATT, VAL, EVENT, MSG> {
    element_ns(None, tag, attrs, children)
}

/// create a virtual node with namespace, tag, attrs and children
#[inline]
pub fn element_ns<NS, TAG, ATT, VAL, EVENT, MSG>(
    namespace: Option<NS>,
    tag: TAG,
    attrs: Vec<Attribute<NS, ATT, VAL, EVENT, MSG>>,
    children: Vec<Node<NS, TAG, ATT, VAL, EVENT, MSG>>,
) -> Node<NS, TAG, ATT, VAL, EVENT, MSG> {
    Node::Element(Element::new(namespace, tag, attrs, children))
}

/// Create a textnode element
#[inline]
pub fn text<S, NS, TAG, ATT, VAL, EVENT, MSG>(s: S) -> Node<NS, TAG, ATT, VAL, EVENT, MSG>
where
    S: ToString,
    ATT: Clone,
{
    Node::Text(s.to_string())
}