render/lib.rs
1//! > 🔏 A safe and simple template engine with the ergonomics of JSX
2//!
3//! `render` itself is a combination of traits, structs and macros that together unify and
4//! boost the experience of composing tree-shaped data structures. This works best with HTML and
5//! XML rendering, but can work with other usages as well, like ReasonML's [`Pastel`](https://reason-native.com/docs/pastel/) library for terminal colors.
6//!
7//! # How?
8//!
9//! A renderable component is a struct that implements the `Render` trait. There
10//! are multiple macros that provide a better experience implementing Renderable:
11//!
12//! * `#[component]` for defining components using a function
13//! * `rsx!` for composing elements with JSX ergonomics
14//! * `html!` for composing elements and render them to a string
15//!
16//! # Why is this different from...
17//!
18//! ## `handlebars`?
19//!
20//! Handlebars is an awesome spec that lets us devs define templates and work
21//! seemlessly between languages and frameworks. Unfortunately, it does not guarantee any of Rust's
22//! type-safety, due to its spec. This forces you to write tests for validating types for your views, like you would in a dynamically typed language. These tests weren't necessary in a type-safe language like Rust — but Handlebars is JSON-oriented, which doesn't comply Rust's type system.
23//!
24//! `render` provides the same level of type-safety Rust provides, with no compromises of
25//! ergonomics or speed.
26//!
27//! ## `typed-html`?
28//!
29//! `typed-html` is a wonderful library. Unfortunately, it focused its power in strictness of the HTML spec itself, and doesn't allow arbitrary compositions of custom elements.
30//!
31//! `render` takes a different approach. For now, HTML is not typed at all. It can get any key and get any string value. The main focus is custom components, so you can create a composable and declarative template with no runtime errors.
32//!
33//! # Usage
34//!
35//! > Note: `render` needs the `nightly` Rust compiler, for now, so it will have hygienic macros.
36//!
37//! This means you will need to add the following feature flag in the root of your `lib.rs`/`main.rs`:
38//!
39//! ```rust
40//! #![feature(proc_macro_hygiene)]
41//! ```
42//!
43//! ## Simple HTML rendering
44//!
45//! In order to render a simple HTML fragment into a `String`, use the `rsx!` macro to generate a
46//! component tree, and call `render` on it:
47//!
48//! ```rust
49//! #![feature(proc_macro_hygiene)]
50//! # use pretty_assertions::assert_eq;
51//!
52//! use render::{rsx, Render};
53//!
54//! let tree = rsx! {
55//! <div>
56//! <h1>{"Hello!"}</h1>
57//! <p>{"Hello world!"}</p>
58//! </div>
59//! };
60//!
61//! assert_eq!(tree.render(), "<div><h1>Hello!</h1><p>Hello world!</p></div>");
62//! ```
63//!
64//! Because this is so common, there's another macro called `html!` that calls `rsx!` to generate
65//! a component tree, and then calls `render` on it. Most of the time, you'll find yourself using
66//! the `rsx!` macro to compose arbitrary components, and only calling `html!` when you need a
67//! String output, when sending a response or generating a Markdown file.
68//!
69//! In Render, attributes and plain strings are escaped using the `render::html_escaping` module. In order to
70//! use un-escaped values so you can dangerously insert raw HTML, use the `raw!` macro around your
71//! string:
72//!
73//! ```rust
74//! #![feature(proc_macro_hygiene)]
75//! # use pretty_assertions::assert_eq;
76//!
77//! use render::{html, raw};
78//!
79//! let tree = html! {
80//! <div>
81//! <p>{"<Hello />"}</p>
82//! <p>{raw!("<Hello />")}</p>
83//! </div>
84//! };
85//!
86//! assert_eq!(tree, "<div><p><Hello /></p><p><Hello /></p></div>");
87//! ```
88//!
89//! ## Custom components
90//!
91//! Render's greatest ability is to provide type-safety along with custom renderable components.
92//! Introducing new components is as easy as defining a function that returns a `Render` value.
93//!
94//! In order to build up components from other components or HTML nodes, you can use the `rsx!`
95//! macro, which generates a `Render` component tree:
96//!
97//! ```rust
98//! #![feature(proc_macro_hygiene)]
99//! # use pretty_assertions::assert_eq;
100//!
101//! use render::{component, rsx, html};
102//!
103//! #[component]
104//! fn Heading<'title>(title: &'title str) {
105//! rsx! { <h1 class={"title"}>{title}</h1> }
106//! }
107//!
108//! let rendered_html = html! {
109//! <Heading title={"Hello world!"} />
110//! };
111//!
112//! assert_eq!(rendered_html, r#"<h1 class="title">Hello world!</h1>"#);
113//! ```
114//!
115//! If you pay close attention, you see that the function `Heading` is:
116//!
117//! * declared with an uppercase. Underneath, it generates a struct with the same name, and
118//! implements the `Render` trait on it.
119//! * does not have a return type. This is because everything is written to a writer, for
120//! performance reasons.
121//!
122//! ### Full example
123//!
124//! ```rust
125//! #![feature(proc_macro_hygiene)]
126//!
127//! // A simple HTML 5 doctype declaration
128//! use render::html::HTML5Doctype;
129//! use render::{
130//! // A macro to create components
131//! component,
132//! // A macro to compose components in JSX fashion
133//! rsx,
134//! // A macro to render components in JSX fashion
135//! html,
136//! // A trait for custom components
137//! Render,
138//! };
139//!
140//! // This can be any layout we want
141//! #[component]
142//! fn Page<'a, Children: Render>(title: &'a str, children: Children) {
143//! rsx! {
144//! <>
145//! <HTML5Doctype />
146//! <html>
147//! <head><title>{title}</title></head>
148//! <body>
149//! {children}
150//! </body>
151//! </html>
152//! </>
153//! }
154//! }
155//!
156//! // This can be a route in Rocket, the web framework,
157//! // for instance.
158//! pub fn some_page(user_name: &str) -> String {
159//! html! {
160//! <Page title={"Home"}>
161//! {format!("Welcome, {}", user_name)}
162//! </Page>
163//! }
164//! }
165//!
166//! # use pretty_assertions::assert_eq;
167//! # let actual = some_page("Gal");
168//! # let expected = concat!(
169//! # "<!DOCTYPE html>",
170//! # "<html>",
171//! # "<head><title>Home</title></head>",
172//! # "<body>",
173//! # "Welcome, Gal",
174//! # "</body>",
175//! # "</html>"
176//! # );
177//! # assert_eq!(actual, expected);
178//! ```
179
180#![feature(proc_macro_hygiene)]
181
182pub mod fragment;
183pub mod html;
184pub mod html_escaping;
185mod render;
186mod simple_element;
187mod text_element;
188
189pub use self::render::Render;
190pub use fragment::Fragment;
191pub use render_macros::{component, html, rsx};
192pub use simple_element::SimpleElement;
193pub use text_element::Raw;