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, <world>");
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}