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 web;
49
50/// The `<!DOCTYPE html>` string literal.
51///
52/// # Example
53///
54/// ```
55/// use gen_html::{DOCTYPE, html};
56///
57/// # let markup =
58/// html! {
59///     (DOCTYPE)
60///     h1 { "hello world" }
61/// }
62/// # ;
63/// # assert_eq!(markup.to_string(), "<!DOCTYPE html><h1>hello world</h1>");
64/// ```
65pub const DOCTYPE: Raw<&str> = Raw("<!DOCTYPE html>");
66
67/// Generate HTML with `maud`-like syntax.
68///
69/// ## Elements
70///
71/// Normal HTML elements are written with curly-braces.
72///
73/// ```
74/// # let markup = gen_html::
75/// html! {
76///     h1 { "Lorem ipsum" }
77///     p { "Lorem ipsum dolor sit amet." }
78/// }
79/// # ; assert_eq!(markup.to_string(), "<h1>Lorem ipsum</h1><p>Lorem ipsum dolor sit amet.</p>");
80/// ```
81///
82/// Void elements require a trailing semicolon.
83///
84/// ```
85/// # let markup = gen_html::
86/// html! {
87///     p {
88///         "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
89///         br ;
90///         "Praesent pharetra urna ex."
91///     }
92/// }
93/// # ; assert_eq!(
94/// #     markup.to_string(),
95/// #     "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br>Praesent pharetra urna ex.</p>",
96/// # );
97/// ```
98///
99/// ## Attributes
100///
101/// Add an attribute with `attr_name: "value"` syntax.
102///
103/// ```
104/// # let markup = gen_html::
105/// html! {
106///     img src: "logo.svg" ;
107///     // gets automatically changed to `data-cooldown`
108///     button data_cooldown: "5s" onclick: "..." { "Click me" }
109/// }
110/// # ; assert_eq!(
111/// #     markup.to_string(),
112/// #     r#"<img src="logo.svg"><button data-cooldown="5s" onclick="...">Click me</button>"#,
113/// # );
114/// ```
115///
116/// You may omit the value to create an empty attribute.
117///
118/// ```
119/// # let markup = gen_html::
120/// html! {
121///     p { "I agree to the terms of service." }
122///     input r#type: "checkbox" checked ;
123/// }
124/// # ; assert_eq!(markup.to_string(), r#"<p>I agree to the terms of service.</p><input type="checkbox" checked>"#);
125/// ```
126///
127/// Specifying the `id` and `class` can be done using the attribute syntax or their respective
128/// shorthand `@` and `.`. The `@` character was chosen due to rust syntax using `#""#` for string
129/// literals.
130///
131/// ```
132/// # let markup = gen_html::
133/// html! {
134///     div id: "my-div-1" class: "flex flex-col gap-2" { "..." }
135///     div @"my-div-2" ."flex flex-col gap-2" { "..." }
136/// }
137/// # ; assert_eq!(
138/// #     markup.to_string(),
139/// #     r#"<div id="my-div-1" class="flex flex-col gap-2">...</div><div id="my-div-2" class="flex flex-col gap-2">...</div>"#,
140/// # );
141/// ```
142///
143/// ## Inserting expressions
144///
145/// Insert arbitrary expression of type that implements the [`Render`] trait with `(expr)` syntax.
146///
147/// ```
148/// # let name = "Bob";
149/// # let class_list = "class-1 class-2";
150/// # let id = "my-id";
151/// # let markup = gen_html::
152/// html! {
153///     p {
154///         "Hello " (name)
155///     }
156///     img src: (format!("assets/{name}/profile-picture.png")) ;
157///     // Also with the shorthand syntax
158///     div @(id) .(class_list) {}
159/// }
160/// # ; assert_eq!(
161/// #     markup.to_string(),
162/// #     r#"<p>Hello Bob</p><img src="assets/Bob/profile-picture.png"><div id="my-id" class="class-1 class-2"></div>"#,
163/// # );
164/// ```
165///
166/// ## Control structures
167///
168/// Conditional rendering with `if`.
169///
170/// ```
171/// # let age = 19;
172/// # let markup = gen_html::
173/// html! {
174///     if age < 18 {
175///         p { "You're not old enough" }
176///     } else if age == 18 {
177///         p { "Just in time" }
178///     } else {
179///         p { "Hi" }
180///     }
181/// }
182/// # ; assert_eq!(markup.to_string(), "<p>Hi</p>");
183/// ```
184///
185/// `if let` is also supported.
186///
187/// ```
188/// # let name = Some("Steve");
189/// # _ = gen_html::
190/// html! {
191///     if let Some(name) = name {
192///         (name)
193///     } else {
194///         "stranger"
195///     }
196/// }
197/// # ;
198/// ```
199///
200/// Pattern matching using `match`.
201///
202/// ```
203/// # let age = 23;
204/// # _ = gen_html::
205/// html! {
206///     match age {
207///         ..18 => "You're not old enough",
208///         18.. => p { "Hello world" }
209///     }
210/// }
211/// # ;
212/// ```
213///
214/// Use `for` to loop over elements of an iterator.
215///
216/// ```
217/// # let titles = ["Titanic"];
218/// # use gen_html::html;
219/// # _ =
220/// html! {
221///     h1 { "Available movies" }
222///     ul {
223///         for title in titles {
224///             li { (title) }
225///         }
226///     }
227/// }
228/// # ;
229/// ```
230pub use gen_html_proc::html;
231
232pub use escape::Escaped;
233pub use render::{Raw, Render, RenderFn, render_fn};