~repos /tide-jsx

#rust#proc-macro#jsx

git clone https://pyrossh.dev/repos/tide-jsx.git

Tide + JSX


0df6beda Gal Schlezinger

6 years ago
Better docs
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()