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};