html_types/
helpers.rs

1///! Module with some extra constructors using public interfaces
2///! Not strictly necessary to use, but does eliminate some repetitive tasks
3use crate::{
4    attributes,
5    node::{Comment, Element, ElementType, Node},
6    tag::Tag,
7    text::Text,
8    url::Url,
9};
10use attributes::{Attribute, Value};
11use std::collections::HashMap;
12
13impl<'a> Node<'a> {
14    /// Creates a comment as a Node from the supplied string
15    pub fn comment(text: &str) -> Self {
16        let comment: Comment = text.to_string().into();
17        comment.into()
18    }
19
20    /// Creates a text element as a node
21    pub fn text(text: &str) -> Self {
22        let text: Text = Text::create(text);
23        text.into()
24    }
25}
26
27impl<'a> Element<'a, ()> {
28    /// An external stylesheet (link tag)
29    pub fn external_style(url: Url) -> Self {
30        let mut el = Element::<()>::create(Tag::LINK);
31        let url: Value<'a> = url.into();
32        el.set_attribute(Attribute::REL, Value::STYLESHEET);
33        el.set_attribute(Attribute::HREF, url);
34        el
35    }
36}
37
38impl<'a> Element<'a, Vec<Node<'a>>> {
39    /// Pushes a child not into the element
40    /// Note: you can also do this yourself, but this is nicer
41    /// as it will coerce anything that can be a node into a node
42    pub fn push<N>(&mut self, node: N) -> ()
43    where
44        N: Into<Node<'a>>,
45    {
46        let node = node.into();
47        self.children.push(node);
48    }
49
50    /// Helper function to create a link
51    /// given the label and url
52    pub fn anchor(url: Value<'a>, label: Text) -> Self {
53        // Note: in future URL should be more strongly typed?
54        let mut element = Element::<Vec<Node>>::create(Tag::A);
55        element.set_attribute(Attribute::HREF, url);
56        element.push(label);
57        element.into()
58    }
59
60    /// Creates a body element. Note consumes the children vector
61    pub fn body<N>(children: Vec<N>) -> Self
62    where
63        N: Into<Node<'a>>,
64    {
65        let mut el = Element::<Vec<Node>>::create(Tag::BODY);
66        for child in children {
67            el.push(child);
68        }
69        el
70    }
71
72    /// Creates the html element
73    /// Helper here assumes you have a body and header.
74    /// as thats's used pretty much always
75    pub fn html(
76        lang: Value<'a>,
77        header: Element<'a, Vec<Node<'a>>>,
78        body: Element<'a, Vec<Node<'a>>>,
79    ) -> Self {
80        let mut el = Element::<Vec<Node>>::create(Tag::HTML);
81        el.set_attribute(Attribute::LANG, lang);
82        el.push(header);
83        el.push(body);
84        el
85    }
86
87    /// Creates a html title, with the supplied text
88    pub fn title(title: Text) -> Self {
89        let mut el = Element::<Vec<Node>>::create(Tag::TITLE);
90        el.push(title);
91        el
92    }
93
94    /// Creates an inline script in to be used in the head section
95    pub fn inline_script(text: Text) -> Self {
96        let mut el = Element::<Vec<Node>>::create(Tag::SCRIPT);
97        el.push(text);
98        el
99    }
100
101    /// An external script tag
102    pub fn external_script(url: Url) -> Self {
103        let mut el = Element::<Vec<Node>>::create(Tag::SCRIPT);
104        let url: Value<'a> = url.into();
105        el.set_attribute(Attribute::SRC, url);
106        el
107    }
108
109    /// Creates an inline script in to be used in the head section
110    pub fn inline_style(text: Text) -> Self {
111        let mut el = Element::<Vec<Node>>::create(Tag::STYLE);
112        el.push(text);
113        el
114    }
115
116    /// Creates the head section of a document
117    pub fn head() -> Self {
118        Element::<Vec<Node>>::create(Tag::HEAD)
119    }
120
121    /// Creats a main, used to hold bulk of page
122    pub fn main() -> Self {
123        Element::<Vec<Node>>::create(Tag::MAIN)
124    }
125}
126
127impl<'a, T> Element<'a, T>
128where
129    T: ElementType,
130{
131    /// Creates a typical element with children, from the
132    /// provided tag name. This is the typical case
133    pub fn create(name: Tag<'a>) -> Self {
134        let attributes = HashMap::default();
135        let children = T::default();
136        let element = Element {
137            name,
138            attributes,
139            children,
140        };
141        element
142    }
143
144    /// Sets the supplied attribute as a 'boolean' attribute
145    /// This means it will just appear in the html, and will not render
146    /// with a =value eg <div editable></div>
147    pub fn set_bool_attribute(&mut self, key: Attribute<'a>) {
148        self.attributes.insert(key, None);
149    }
150
151    /// Tests whether the attribute exists. Note this returns true
152    /// for bool attributes, and valued attributes
153    pub fn has_bool_attribute(&self, key: &Attribute<'a>) -> bool {
154        let attr = self.attributes.get(key);
155        let result = attr.is_some();
156        result
157    }
158
159    /// Tests whether the attribute exists. Note this returns false
160    /// for bool attributes, and true for valued attributes
161    pub fn has_attribute(&self, key: &Attribute<'a>) -> bool {
162        let attr = self.attributes.get(key);
163        let result = attr.map(|x| x.is_some()).unwrap_or(false);
164        result
165    }
166
167    /// Sets the attribute to the supplied value
168    /// Note: sugar over wrapping the value with some.
169    pub fn set_attribute(&mut self, key: Attribute<'a>, value: Value<'a>) {
170        self.attributes.insert(key, Some(value));
171    }
172
173    /// Returns the value of the supplied key
174    /// note for bool attributes, this will still be none
175    pub fn get_attribute_value(&self, key: &Attribute<'a>) -> &Option<Value<'a>> {
176        match self.attributes.get(key) {
177            None => &None,
178            Some(value) => value,
179        }
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186    use crate::attributes::Attribute;
187
188    #[test]
189    fn has_attribute_bool() {
190        let mut el = Element::<()>::create(Tag::BODY);
191        el.attributes.insert(Attribute::ID, None);
192        let result = el.has_bool_attribute(&Attribute::ID);
193        assert!(result);
194    }
195
196    #[test]
197    fn has_attribute() {
198        let value = Value::STYLESHEET;
199        let mut el = Element::<()>::create(Tag::BODY);
200        el.attributes.insert(Attribute::ID, Some(value));
201        let result = el.has_attribute(&Attribute::ID);
202        assert!(result);
203    }
204
205    #[test]
206    fn get_attribute_value() {
207        let value = Value::STYLESHEET;
208        let mut el = Element::<()>::create(Tag::BODY);
209        el.attributes.insert(Attribute::ID, Some(value));
210        let result = el.get_attribute_value(&Attribute::ID);
211        let expected = &Some(Value::STYLESHEET);
212        assert_eq!(result, expected);
213    }
214}