0df6beda
—
Gal Schlezinger 6 years ago
Better docs
- render/src/lib.rs +62 -0
- render/src/renderable.rs +49 -0
- render/src/text_element.rs +4 -0
render/src/lib.rs
CHANGED
|
@@ -1,3 +1,65 @@
|
|
|
1
|
+
//! > 🔏 A safe and simple template engine with the ergonomics of JSX
|
|
2
|
+
//!
|
|
3
|
+
//! The `Renderable` trait contains a simple function that returns `String`. This is very handy for type-safe HTML templates, but can also work for writing tree-like terminal coloring mechanism like ReasonML's [Pastel](https://reason-native.com/docs/pastel/).
|
|
4
|
+
//!
|
|
5
|
+
//! ## Why this is different from `typed-html`?
|
|
6
|
+
//!
|
|
7
|
+
//! `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.
|
|
8
|
+
//!
|
|
9
|
+
//! `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.
|
|
10
|
+
//!
|
|
11
|
+
//! ## Usage
|
|
12
|
+
//!
|
|
13
|
+
//! ```rust
|
|
14
|
+
//! #![feature(proc_macro_hygiene)]
|
|
15
|
+
//!
|
|
16
|
+
//! // A simple HTML 5 doctype declaration
|
|
17
|
+
//! use render::html::HTML5Doctype;
|
|
18
|
+
//! use render::{
|
|
19
|
+
//! // A macro to compose components in JSX fashion
|
|
20
|
+
//! html,
|
|
21
|
+
//! // A component that just render its children
|
|
22
|
+
//! Fragment,
|
|
23
|
+
//! // A trait for custom components
|
|
24
|
+
//! Renderable,
|
|
25
|
+
//! };
|
|
26
|
+
//!
|
|
27
|
+
//! // This can be any layout we want
|
|
28
|
+
//! #[derive(Debug)]
|
|
29
|
+
//! struct Page<'a, T: Renderable> {
|
|
30
|
+
//! title: &'a str,
|
|
31
|
+
//! children: T,
|
|
32
|
+
//! }
|
|
33
|
+
//!
|
|
34
|
+
//! // Implementing `Renderable` gives the ability to compose
|
|
35
|
+
//! // components
|
|
36
|
+
//! impl<'a, T: Renderable> Renderable for Page<'a, T> {
|
|
37
|
+
//! fn render(self) -> String {
|
|
38
|
+
//! html! {
|
|
39
|
+
//! <Fragment>
|
|
40
|
+
//! <HTML5Doctype />
|
|
41
|
+
//! <html>
|
|
42
|
+
//! <head><title>{self.title}</title></head>
|
|
43
|
+
//! <body>
|
|
44
|
+
//! {self.children}
|
|
45
|
+
//! </body>
|
|
46
|
+
//! </html>
|
|
47
|
+
//! </Fragment>
|
|
48
|
+
//! }
|
|
49
|
+
//! }
|
|
50
|
+
//! }
|
|
51
|
+
//!
|
|
52
|
+
//! // This can be a route in Rocket, the web framework,
|
|
53
|
+
//! // for instance.
|
|
54
|
+
//! pub fn some_page(user_name: &str) -> String {
|
|
55
|
+
//! html! {
|
|
56
|
+
//! <Page title={"Home"}>
|
|
57
|
+
//! {format!("Welcome, {}", user_name)}
|
|
58
|
+
//! </Page>
|
|
59
|
+
//! }
|
|
60
|
+
//! }
|
|
61
|
+
//! ```
|
|
62
|
+
|
|
1
63
|
pub mod fragment;
|
|
2
64
|
pub mod html;
|
|
3
65
|
mod renderable;
|
render/src/renderable.rs
CHANGED
|
@@ -4,33 +4,72 @@ pub trait Renderable: core::fmt::Debug + Sized {
|
|
|
4
4
|
///
|
|
5
5
|
/// Mostly done using the `html!` macro to generate strings
|
|
6
6
|
/// by composing tags.
|
|
7
|
+
///
|
|
8
|
+
/// A simple implementation:
|
|
9
|
+
///
|
|
10
|
+
/// ```rust
|
|
11
|
+
/// # #![feature(proc_macro_hygiene)]
|
|
12
|
+
/// # use render_macros::html;
|
|
13
|
+
/// # use render::Renderable;
|
|
14
|
+
/// # use pretty_assertions::assert_eq;
|
|
15
|
+
/// #[derive(Debug)]
|
|
16
|
+
/// struct Header<'t> { title: &'t str }
|
|
17
|
+
///
|
|
18
|
+
/// impl<'t> Renderable for Header<'t> {
|
|
19
|
+
/// fn render(self) -> String {
|
|
20
|
+
/// html! {
|
|
21
|
+
/// <h1>{self.title}</h1>
|
|
22
|
+
/// }
|
|
23
|
+
/// }
|
|
24
|
+
/// }
|
|
25
|
+
///
|
|
26
|
+
/// // Then you can use it with
|
|
27
|
+
///
|
|
28
|
+
/// let rendered_html = html! {
|
|
29
|
+
/// <Header title={"Hello world!"} />
|
|
30
|
+
/// };
|
|
31
|
+
///
|
|
32
|
+
/// # assert_eq!(rendered_html, "<h1>Hello world!</h1>");
|
|
33
|
+
/// ```
|
|
34
|
+
///
|
|
35
|
+
/// ## Children
|
|
36
|
+
///
|
|
37
|
+
/// `children` is a special field that will be populated with other `Renderable` if any children was provided, by both the [`html!`] and the [`rsx!`] macros.
|
|
38
|
+
///
|
|
39
|
+
/// [`html!`]: ../render_macros/macro.html.html
|
|
40
|
+
/// [`rsx!`]: ../render_macros/macro.rsx.html
|
|
7
41
|
fn render(self) -> String;
|
|
8
42
|
}
|
|
9
43
|
|
|
44
|
+
/// Renders an empty string
|
|
10
45
|
impl Renderable for () {
|
|
11
46
|
fn render(self) -> String {
|
|
12
47
|
"".to_string()
|
|
13
48
|
}
|
|
14
49
|
}
|
|
15
50
|
|
|
51
|
+
/// Renders `A` and then `B`
|
|
16
52
|
impl<A: Renderable, B: Renderable> Renderable for (A, B) {
|
|
17
53
|
fn render(self) -> String {
|
|
18
54
|
format!("{}{}", self.0.render(), self.1.render())
|
|
19
55
|
}
|
|
20
56
|
}
|
|
21
57
|
|
|
58
|
+
/// Renders `A`, `B`, and then `C`
|
|
22
59
|
impl<A: Renderable, B: Renderable, C: Renderable> Renderable for (A, B, C) {
|
|
23
60
|
fn render(self) -> String {
|
|
24
61
|
((self.0, self.1), self.2).render()
|
|
25
62
|
}
|
|
26
63
|
}
|
|
27
64
|
|
|
65
|
+
/// Renders `A`, `B`, `C` and then `D`
|
|
28
66
|
impl<A: Renderable, B: Renderable, C: Renderable, D: Renderable> Renderable for (A, B, C, D) {
|
|
29
67
|
fn render(self) -> String {
|
|
30
68
|
((self.0, self.1), (self.2, self.3)).render()
|
|
31
69
|
}
|
|
32
70
|
}
|
|
33
71
|
|
|
72
|
+
/// Renders the `T` or an empty string
|
|
34
73
|
impl<T: Renderable> Renderable for Option<T> {
|
|
35
74
|
fn render(self) -> String {
|
|
36
75
|
match self {
|
|
@@ -39,3 +78,13 @@ impl<T: Renderable> Renderable for Option<T> {
|
|
|
39
78
|
}
|
|
40
79
|
}
|
|
41
80
|
}
|
|
81
|
+
|
|
82
|
+
/// Renders `O` or `E`
|
|
83
|
+
impl<O: Renderable, E: Renderable> Renderable for Result<O, E> {
|
|
84
|
+
fn render(self) -> String {
|
|
85
|
+
match self {
|
|
86
|
+
Err(e) => e.render(),
|
|
87
|
+
Ok(o) => o.render(),
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
render/src/text_element.rs
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
use crate::Renderable;
|
|
2
2
|
|
|
3
|
+
/// Renders the string
|
|
4
|
+
/// > TODO: this should escape HTML!
|
|
3
5
|
impl Renderable for String {
|
|
4
6
|
fn render(self) -> String {
|
|
5
7
|
self
|
|
6
8
|
}
|
|
7
9
|
}
|
|
8
10
|
|
|
11
|
+
/// Renders the string
|
|
12
|
+
/// > TODO: this should escape HTML!
|
|
9
13
|
impl Renderable for &str {
|
|
10
14
|
fn render(self) -> String {
|
|
11
15
|
self.to_string()
|