domafic/
tags.rs

1/// Tags, such as `div` or `table`.
2///
3/// To create a `Tag` `DomNode`, simply import the tag function
4/// and call it with a type that implements `Into<TagProperties>`.
5///
6/// Example:
7///
8/// TODO
9
10use {DomNode, DomNodes, DomValue, KeyValue, Listeners};
11use processors::{DomNodeProcessor, EmptyListeners};
12
13use opt_std::marker::PhantomData;
14
15/// Properties used to create a `Tag` `DomNode`.
16///
17/// This is primarily used as an input (via `Into<TagProperties>`) for the various tag functions.
18/// Note the large number of `From/Into` impls for this struct. Thes allows users to avoid fully
19/// specifying all the fields for `TagProperties` by simply calling the tag function with the
20/// appropriate combination of listeners, attributes, and children.
21///
22/// Note that multiple listeners or multiple children must be grouped into a single tuple.
23pub struct TagProperties<
24    Message,
25    Children: DomNodes<Message>,
26    Attributes: AsRef<[KeyValue]>,
27    Listens: Listeners<Message>>
28{
29    children: Children,
30    key: Option<u32>,
31    attributes: Attributes,
32    listeners: Listens,
33    msg_marker: PhantomData<Message>,
34}
35
36type EmptyAttrs = [KeyValue; 0];
37
38/// Create an attributes (`Attrs`) struct from the given array of key-value pairs.
39///
40/// Use this function to create a tag with a given list of attributes.
41///
42/// Example:
43///
44/// ```rust
45/// use domafic::DomNode;
46/// use domafic::tags::{attributes, div};
47/// use domafic::AttributeValue::Str;
48/// use std::marker::PhantomData;
49///
50/// let div_with_attrs = div((
51///     attributes([("key", Str("value"))]),
52///     // We need to manually mark the message type since it can't be inferred
53///     PhantomData::<()>
54/// ));
55/// assert_eq!(div_with_attrs.get_attribute(0), Some(&("key", Str("value"))));
56/// ```
57pub fn attributes<A: AsRef<[KeyValue]>>(attrs: A) -> Attrs<A> {
58    Attrs(attrs)
59}
60
61/// Wrapper for an array of attributes re
62pub struct Attrs<A: AsRef<[KeyValue]>>(A);
63
64// Just children
65impl<M, C: DomNodes<M>> From<C> for TagProperties<M, C, EmptyAttrs, EmptyListeners> {
66    fn from(nodes: C) -> TagProperties<M, C, EmptyAttrs, EmptyListeners> {
67        TagProperties {
68            children: nodes,
69            key: None,
70            attributes: [],
71            listeners: EmptyListeners,
72            msg_marker: PhantomData,
73        }
74    }
75}
76
77// Just attributes
78impl<M, A: AsRef<[KeyValue]>>
79    From<Attrs<A>> for TagProperties<M, (), A, EmptyListeners>
80{
81    fn from(props: Attrs<A>) -> TagProperties<M, (), A, EmptyListeners> {
82        TagProperties {
83            children: (),
84            key: None,
85            attributes: props.0,
86            listeners: EmptyListeners,
87            msg_marker: PhantomData,
88        }
89    }
90}
91
92// Just Listeners
93impl<M, L: Listeners<M>>
94    From<L> for TagProperties<M, (), EmptyAttrs, L>
95{
96    fn from(props: L) -> TagProperties<M, (), EmptyAttrs, L> {
97        TagProperties {
98            children: (),
99            key: None,
100            attributes: [],
101            listeners: props,
102            msg_marker: PhantomData,
103        }
104    }
105}
106
107// (attributes, children)
108impl<M, C: DomNodes<M>, A: AsRef<[KeyValue]>>
109    From<(Attrs<A>, C)> for TagProperties<M, C, A, EmptyListeners>
110{
111    fn from(props: (Attrs<A>, C)) -> TagProperties<M, C, A, EmptyListeners> {
112        TagProperties {
113            children: props.1,
114            key: None,
115            attributes: (props.0).0,
116            listeners: EmptyListeners,
117            msg_marker: PhantomData,
118        }
119    }
120}
121
122// (attributes, listeners)
123impl<M, A: AsRef<[KeyValue]>, L: Listeners<M>>
124    From<(Attrs<A>, L)> for TagProperties<M, (), A, L>
125{
126    fn from(props: (Attrs<A>, L)) -> TagProperties<M, (), A, L> {
127        TagProperties {
128            children: (),
129            key: None,
130            attributes: (props.0).0,
131            listeners: props.1,
132            msg_marker: PhantomData,
133        }
134    }
135}
136
137// (listeners, children)
138impl<M, C: DomNodes<M>, L: Listeners<M>>
139    From<(L, C)> for TagProperties<M, C, EmptyAttrs, L>
140{
141    fn from(props: (L, C)) -> TagProperties<M, C, EmptyAttrs, L> {
142        TagProperties {
143            children: props.1,
144            key: None,
145            attributes: [],
146            listeners: props.0,
147            msg_marker: PhantomData,
148        }
149    }
150}
151
152// (attributes, listeners, children)
153impl<M, C: DomNodes<M>, A: AsRef<[KeyValue]>, L: Listeners<M>>
154    From<(Attrs<A>, L, C)> for TagProperties<M, C, A, L>
155{
156    fn from(props: (Attrs<A>, L, C)) -> TagProperties<M, C, A, L> {
157        TagProperties {
158            children: props.2,
159            key: None,
160            attributes: (props.0).0,
161            listeners: props.1,
162            msg_marker: PhantomData,
163        }
164    }
165}
166
167/// A tag element, such as `div` or `span`.
168#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
169pub struct Tag<
170    Message,
171    Children: DomNodes<Message>,
172    Attributes: AsRef<[KeyValue]>,
173    L: Listeners<Message>>
174{
175    tagname: &'static str,
176    children: Children,
177    key: Option<u32>,
178    attributes: Attributes,
179    listeners: L,
180    msg_marker: PhantomData<Message>,
181}
182
183impl<
184    M,
185    C: DomNodes<M>,
186    A: AsRef<[KeyValue]>,
187    L: Listeners<M>> DomNodes<M> for Tag<M, C, A, L>
188{
189    fn process_all<'a, P: DomNodeProcessor<'a, M>>(&'a self, acc: &mut P::Acc) -> Result<(), P::Error> {
190        P::get_processor()(acc, self)
191    }
192}
193impl<M, C: DomNodes<M>, A: AsRef<[KeyValue]>, L: Listeners<M>> DomNode<M> for Tag<M, C, A, L> {
194    type Children = C;
195    type Listeners = L;
196    type WithoutListeners = Tag<M, C, A, EmptyListeners>;
197    fn key(&self) -> Option<u32> { self.key }
198    fn get_attribute(&self, index: usize) -> Option<&KeyValue> {
199        self.attributes.as_ref().get(index)
200    }
201    fn children(&self) -> &Self::Children {
202        &self.children
203    }
204    fn listeners(&self) -> &Self::Listeners {
205        &self.listeners
206    }
207    fn children_and_listeners(&self) -> (&Self::Children, &Self::Listeners) {
208        (&self.children, &self.listeners)
209    }
210    fn split_listeners(self) -> (Self::WithoutListeners, Self::Listeners) {
211        let Tag { tagname, children, key, attributes, listeners, msg_marker } = self;
212        (
213            Tag {
214                tagname: tagname,
215                children: children,
216                key: key,
217                attributes: attributes,
218                listeners: EmptyListeners,
219                msg_marker: msg_marker,
220            },
221            listeners
222        )
223    }
224    fn value(&self) -> DomValue {
225        DomValue::Element {
226            tag: self.tagname,
227        }
228    }
229}
230
231#[cfg(any(feature = "use_std", test))]
232use std::fmt;
233#[cfg(any(feature = "use_std", test))]
234impl<M, C, A, L> fmt::Display for Tag<M, C, A, L>
235    where
236    C: DomNodes<M>,
237    A: AsRef<[KeyValue]>,
238    L: Listeners<M>
239{
240    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
241        self.displayable().fmt(formatter)
242    }
243}
244
245
246macro_rules! impl_tags {
247    ($($tagname:ident),*) => { $(
248        /// Creates a tag of the given type.
249        ///
250        /// Note the use of `Into<TagProperties>`. This allows for a wide variety of input
251        /// parameters such as `div(())`, `div(...children...)`,
252        /// `div((...attributes..., ...children..))`, `div((...attributes..., ...listeners...))`
253        /// and more.
254        pub fn $tagname<
255            M,
256            C: DomNodes<M>,
257            A: AsRef<[KeyValue]>,
258            L: Listeners<M>,
259            T: Into<TagProperties<M, C, A, L>>
260            >(properties: T)
261            -> Tag<M, C, A, L>
262        {
263            let TagProperties {
264                children,
265                key,
266                attributes,
267                listeners,
268                msg_marker,
269            } = properties.into();
270
271            Tag {
272                tagname: stringify!($tagname),
273                children: children,
274                key: key,
275                attributes: attributes,
276                listeners: listeners,
277                msg_marker: msg_marker,
278            }
279        }
280    )* }
281}
282
283impl_tags!(
284    a, abbr, acronym, address, applet, area, article, aside, audio, b, base, basefont, bdi,
285    bdo, big, blockquote, body, br, button, canvas, caption, center, cite, code, col, colgroup,
286    datalist, dd, del, details, dfn, dialog, dir, div, dl, dt, em, embed, fieldset,
287    figcaption, figure, font, footer, form, frame, framset, h1, h2, h3, h4, h5, h6, head,
288    header, hr, i, iframe, img, input, ins, kbd, keygen, label, legend, li, link, main, map,
289    mark, menu, menuitem, meta, meter, nav, noframes, noscript, object, ol, optgroup, option,
290    output, p, param, pre, progress, q, rp, rt, ruby, s, samp, script, section, select, small,
291    source, span, strike, strong, style, sub, summary, sup, table, tbody, td, textarea, tfoot,
292    th, thead, time, title, tr, track, tt, u, ul, var, video, wbr
293);