gen_html/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3//! `gen-html` is a templating library for generating HTML from Rust.
4//!
5//! # Features
6//!
7//! - **Fast** — [`html!`] macro generates code that is as fast as writing to a string by hand.
8//! - **Conditional rendering** — you can use `if`, `for` and `match` inside your templates.
9//! - **Automatic escaping**, however you can opt-out using [`Raw<T>`].
10//! - **Type safety** — HTML tags and attributes are checked at compile time.
11//! - Integration with the rust web ecosystem (`axum`, `actix-web`).
12//!
13//! # Example
14//!
15//! ```
16//! use gen_html::html;
17//!
18//! let markup = html! {
19//! for i in 1..=3 {
20//! span { (i.to_string()) }
21//! }
22//! };
23//!
24//! println!("{}", markup);
25//! # assert_eq!(markup.to_string(), "<span>1</span><span>2</span><span>3</span>");
26//! ```
27//!
28//! The [`html!`] macro roughly expands to this code.
29//!
30//! ```
31//! use gen_html::{Render, render_fn};
32//!
33//! let markup = render_fn(|f| {
34//! for i in 1..=3 {
35//! f.write_str("<span>")?;
36//! (&i.to_string()).render_to(f)?;
37//! f.write_str("</span>")?;
38//! }
39//! Ok(())
40//! });
41//!
42//! /* ... */
43//! # assert_eq!(markup.render().0, "<span>1</span><span>2</span><span>3</span>");
44//! ```
45
46mod escape;
47mod render;
48mod value;
49mod web;
50
51/// The `<!DOCTYPE html>` string literal.
52///
53/// # Example
54///
55/// ```
56/// use gen_html::{DOCTYPE, html};
57///
58/// # let markup =
59/// html! {
60/// (DOCTYPE)
61/// h1 { "hello world" }
62/// }
63/// # ;
64/// # assert_eq!(markup.to_string(), "<!DOCTYPE html><h1>hello world</h1>");
65/// ```
66pub const DOCTYPE: Raw<&str> = Raw("<!DOCTYPE html>");
67
68/// Generate HTML with `maud`-like syntax.
69///
70/// The `html!` macro allows you to write HTML using a `maud`-like syntax while
71/// being as efficient as writing to a [`String`] by hand.
72///
73/// # HTML elements
74///
75/// Elements are written using their tag name followed by curly braces
76/// containing their children.
77///
78/// ```
79/// # use gen_html::html;
80/// # let markup =
81/// html! {
82/// h1 { "Hello world" }
83/// p { "This is a paragraph." }
84/// }
85/// # ;
86///
87/// # assert_eq!(
88/// # markup.to_string(),
89/// # "<h1>Hello world</h1><p>This is a paragraph.</p>"
90/// # );
91/// ```
92///
93/// Void elements (elements without closing tags) use a trailing semicolon.
94///
95/// ```
96/// # use gen_html::html;
97/// # let markup =
98/// html! {
99/// "First line"
100/// br;
101/// "Second line"
102/// }
103/// # ;
104/// # assert_eq!(markup.to_string(), "First line<br>Second line");
105/// ```
106///
107/// # Attributes
108///
109/// Attributes are written using `name: value`.
110///
111/// ```
112/// # use gen_html::html;
113/// # let markup =
114/// html! {
115/// a href: "https://example.com" { "Visit site" }
116/// }
117/// # ;
118/// # assert_eq!(
119/// # markup.to_string(),
120/// # r#"<a href="https://example.com">Visit site</a>"#
121/// # );
122/// ```
123///
124/// Boolean attributes may omit the value.
125///
126/// ```
127/// # use gen_html::html;
128/// # let markup =
129/// html! {
130/// input r#type: "checkbox" checked;
131/// }
132/// # ;
133/// # assert_eq!(
134/// # markup.to_string(),
135/// # r#"<input type="checkbox" checked>"#
136/// # );
137/// ```
138///
139/// # Shorthand syntax
140///
141/// Instead of writing `id` and `class` you may use `@` and `.` respectively.
142///
143/// - `@"container"` ==> `id: "container"`
144/// - `."flex gap-2"` ==> `class: "flex gap-2"`
145///
146/// ```
147/// # use gen_html::html;
148/// # let markup =
149/// html! {
150/// div @"container" ."flex gap-2" { "content" }
151/// }
152/// # ;
153/// # assert_eq!(
154/// # markup.to_string(),
155/// # r#"<div id="container" class="flex gap-2">content</div>"#
156/// # );
157/// ```
158///
159/// # Inserting expressions
160///
161/// Use `(expr)` to insert any Rust expression implementing [`Render`].
162///
163/// ```
164/// # use gen_html::html;
165/// let name = "Alice";
166///
167/// # let markup =
168/// html! {
169/// p { "Hello " (name) "!" }
170/// }
171/// # ;
172/// # assert_eq!(markup.to_string(), "<p>Hello Alice!</p>");
173/// ```
174///
175/// Expressions that implement [`Value`] may be used inside attributes. See its documentation for more details.
176///
177/// ```
178/// # use gen_html::html;
179/// let path = "/assets/logo.svg";
180/// let description = "Company logo";
181///
182/// # let markup =
183/// html! {
184/// img src: (path) alt: (description);
185/// }
186/// # ;
187/// # assert_eq!(
188/// # markup.to_string(),
189/// # r#"<img src="/assets/logo.svg" alt="Company logo">"#
190/// # );
191/// ```
192///
193/// # Control structures
194///
195/// ## `if`
196///
197/// ```
198/// # use gen_html::html;
199/// let logged_in = true;
200///
201/// # let markup =
202/// html! {
203/// if logged_in {
204/// p { "Welcome back!" }
205/// } else {
206/// p { "Please log in." }
207/// }
208/// }
209/// # ;
210/// # assert_eq!(markup.to_string(), "<p>Welcome back!</p>");
211/// ```
212///
213/// ## `if let`
214///
215/// ```
216/// # use gen_html::html;
217/// let name = Some("Damian");
218///
219/// # let markup =
220/// html! {
221/// if let Some(name) = name {
222/// (name)
223/// } else {
224/// "stranger"
225/// }
226/// }
227/// # ;
228/// # assert_eq!(markup.to_string(), "Damian");
229/// ```
230///
231/// ## `match`
232///
233/// ```
234/// # use gen_html::html;
235/// let status = 404;
236///
237/// # let markup =
238/// html! {
239/// match status {
240/// 200 => p { "OK" },
241/// 404 => p { "Not found" },
242/// _ => p { "Unknown status" }
243/// }
244/// }
245/// # ;
246/// # assert_eq!(markup.to_string(), "<p>Not found</p>");
247/// ```
248///
249/// ## `for`
250///
251/// ```
252/// # use gen_html::html;
253/// let shows = ["Breaking Bad", "Planet Earth II", "Chernobyl"];
254///
255/// # let markup =
256/// html! {
257/// h1 { "Popular TV shows" }
258/// ul {
259/// for title in shows {
260/// li { (title) }
261/// }
262/// }
263/// }
264/// # ;
265/// # assert_eq!(
266/// # markup.to_string(),
267/// # "<h1>Popular TV shows</h1><ul><li>Breaking Bad</li><li>Planet Earth II</li><li>Chernobyl</li></ul>"
268/// # );
269/// ```
270///
271/// ## `let`
272///
273/// ```
274/// # use gen_html::html;
275/// # let markup =
276/// html! {
277/// let src = "/assets/welcome.svg";
278/// img src: (src);
279///
280/// // You may also provide the type:
281/// let src: &str = "/assets/welcome.svg";
282///
283/// // let Some(src) = ... else { ... };
284/// // ERROR: Only irrefutable patterns are supported
285/// }
286/// # ;
287/// # assert_eq!(
288/// # markup.to_string(),
289/// # "<img src=\"/assets/welcome.svg\">"
290/// # );
291/// ```
292pub use gen_html_proc::html;
293
294pub use escape::Escaped;
295pub use render::{Raw, Render, RenderFn, render_fn};
296pub use value::Value;