write_html/
lib.rs

1
2/*!
3This crate provides a way to write HTML with as little overhead as possible.
4
5> This crate is still in development, and the API is not stable.
6
7# Example
8```
9use write_html::*;
10
11let page = html!(
12    (Doctype)
13    html lang="en" {
14        head {
15            (DefaultMeta)
16            title { "Website!" }
17        }
18        body {
19            h1 #some-id { "H1" }
20            h2 { "H2" }
21            h3 { "H3" }
22            p { "Paragraph" }
23            ol {
24                li { "Item 1" }
25                li { "Item 2" }
26                li style="color: red" { "Item 3" }
27            }
28            footer;
29        }
30    }
31).to_html_string().unwrap();
32```
33*/
34
35//#![warn(missing_docs)]
36
37use std::fmt::Write;
38
39mod attributes;
40mod tag;
41mod html_trait;
42
43pub use attributes::*;
44use escapes::HtmlEscaper;
45pub use tag::*;
46pub use html_trait::*;
47
48pub mod escapes;
49pub mod tags;
50
51pub use write_html_macro::html;
52
53// TODO move
54/// Represents something that is "empty"
55pub struct Empty;
56
57// TODO move
58/// Represents a sum of two types.
59pub struct Sum<A, B>(pub A, pub B);
60
61
62/// Represents an environment that can write HTML.
63///
64/// This trait is implemented for everything that implements [`Write`], for example [`String`].
65pub trait HtmlEnv: Write + Sized {
66
67    /// Writes an [`Html`] into the environment.
68    ///
69    /// # Example
70    /// ```
71    /// use write_html::{HtmlEnv, Html, AsHtml};
72    /// 
73    /// let mut s = String::new();
74    /// s.write_html("Hello, world!".as_html()).unwrap();
75    /// assert_eq!(s, "Hello, world!");
76    /// ```
77    fn write_html(&mut self, html: impl Html) -> Result<&mut Self, std::fmt::Error> {
78        html.write_html(self)?;
79        Ok(self)
80    }
81
82    /// Writes the HTML5 doctype.
83    ///
84    /// # Example
85    /// ```
86    /// use write_html::HtmlEnv;
87    /// 
88    /// let mut s = String::new();
89    /// s.doctype();
90    /// assert_eq!(s, "<!DOCTYPE html>");
91    /// ```
92    fn doctype(&mut self) {
93        write!(self, "<!DOCTYPE html>").unwrap();
94    }
95
96    /// Lets you write text into the HTML document, escaping it as necessary.
97    ///
98    /// # Example
99    /// ```
100    /// use write_html::HtmlEnv;
101    /// use std::fmt::Write;
102    /// 
103    /// let mut s = String::new();
104    /// s.write_html_text().write_str("Hello, <world>").unwrap();
105    /// assert_eq!(s, "Hello, &lt;world&gt;");
106    /// ```
107    fn write_html_text<'s>(&'s mut self) -> HtmlEscaper<'s, Self> {
108        HtmlEscaper::new(self)
109    }
110
111    /// Returns a tag opening, which lets you write attributes and inner HTML.
112    ///
113    /// # Arguments
114    /// * `tag` - The tag name.
115    /// * `compactability` - Whether the tag can be compacted.
116    ///
117    /// # Example
118    /// ```
119    /// use write_html::{HtmlEnv, Compactability};
120    /// use std::fmt::Write;
121    /// 
122    /// let mut s = String::new();
123    /// s.open_tag("h1", Compactability::No).unwrap()
124    ///     .with_attr("id", "my-id").unwrap()
125    ///     .inner_html().unwrap()
126    ///     .write_str("Hello, world!").unwrap();
127    /// assert_eq!(s, "<h1 id=\"my-id\">Hello, world!</h1>");
128    /// ```
129    fn open_tag<'s, 't>(
130        &'s mut self,
131        tag: &'t str, // TODO non-static lifetime
132        compactability: Compactability
133    ) -> Result<TagOpening<'s, 't, Self>, std::fmt::Error> {
134        TagOpening::<'s, 't, Self>::new(tag, self, compactability)
135    }
136
137    fn with_html_writer(self, f: impl FnOnce(&mut Self) -> std::fmt::Result) -> Result<Self, std::fmt::Error> {
138        let mut s = self;
139        f(&mut s)?;
140        Ok(s)
141    }
142}
143
144impl<W: Write> HtmlEnv for W {}
145
146/// Writes the default HTML5 `<meta>` tags.
147pub struct DefaultMeta;
148impl Html for DefaultMeta {
149    fn write_html(self, env: &mut impl HtmlEnv) -> std::fmt::Result {
150        env
151            .write_html(tags::meta(
152                [
153                    ("http-equiv", "X-UA-Compatible"),
154                    ("content", "ie=edge")
155                ],
156                Empty,
157            ))?
158            .write_html(tags::meta(
159                [("charset", "UTF-8")],
160                Empty,
161            ))?
162            .write_html(tags::meta(
163                [
164                    ("name", "viewport"),
165                    ("content", "width=device-width, initial-scale=1.0")
166                ],
167                Empty,
168            )).map(|_| ())
169    }
170}
171
172/// Writes the HTML5 doctype.
173pub struct Doctype;
174impl Html for Doctype {
175    fn write_html(self, env: &mut impl HtmlEnv) -> std::fmt::Result {
176        env.doctype();
177        Ok(())
178    }
179}