1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//! This crate provides the `html!` macro for building HTML documents inside your
//! Rust code using roughly [JSX] compatible syntax.
//!
//! # Quick Preview
//!
//! ```
//! # #![recursion_limit = "128"]
//! # use typed_html::{html, for_events};
//! # use typed_html::dom::{DOMTree, VNode};
//! # use typed_html::types::Metadata;
//! # fn main() {
//! let mut doc: DOMTree<String> = html!(
//!     <html>
//!         <head>
//!             <title>"Hello Kitty"</title>
//!             <meta name=Metadata::Author content="Not Sanrio Co., Ltd"/>
//!         </head>
//!         <body>
//!             <h1>"Hello Kitty"</h1>
//!             <p class="official">
//!                 "She is not a cat. She is a human girl."
//!             </p>
//!             { (0..3).map(|_| html!(
//!                 <p class="emphasis">
//!                     "Her name is Kitty White."
//!                 </p>
//!             )) }
//!             <p class="citation-needed">
//!                 "We still don't know how she eats."
//!             </p>
//!         </body>
//!     </html>
//! );
//! let doc_str = doc.to_string();
//! # }
//! ```
//!
//! # Syntax
//!
//! This macro largely follows [JSX] syntax, but with some differences:
//!
//! * Text nodes must be quoted, because there's only so much Rust's tokeniser can
//!   handle outside string literals. So, instead of `<p>Hello</p>`, you need to
//!   write `<p>"Hello"</p>`. (The parser will throw an error asking you to do this
//!   if you forget.)
//! * Element attributes will accept simple Rust expressions, but the parser has
//!   its limits, as it's not a full Rust parser. You can use literals,
//!   variables, dotted properties, type constructors and single function or
//!   method calls. If you use something the parser isn't currently capable of
//!   handling, it will complain. You can put braces or parentheses around the
//!   expression if the parser doesn't understand
//!   it. You can use any Rust code inside a brace or parenthesis block.
//!
//! # Valid HTML5
//!
//! The macro will only accept valid HTML5 tags, with no tags or attributes marked
//! experimental or obsolete. If it won't accept something you want it to accept, we
//! can discuss it over a pull request (experimental tags and attributes, in
//! particular, are mostly omitted just for brevity, and you're welcome to implement
//! them).
//!
//! The structure validation is simplistic by necessity, as it defers to the type
//! system: a few elements will have one or more required children, and any element
//! which accepts children will have a restriction on the type of the children,
//! usually a broad group as defined by the HTML spec. Many elements have
//! restrictions on children of children, or require a particular ordering of
//! optional elements, which isn't currently validated.
//!
//! # Attribute Values
//!
//! Brace blocks in the attribute value position should return the expected type for
//! the attribute. The type checker will complain if you return an unsupported type.
//! You can also use literals or a few simple Rust expressions as attribute values
//! (see the Syntax section above).
//!
//! The `html!` macro will add an [`.into()`][Into::into] call to the value
//! expression, so that you can use any type that has an [`Into<A>`][Into] trait
//! defined for the actual attribute type `A`.
//!
//! As a special case, if you use a string literal, the macro will instead use the
//! [`FromStr<A>`][FromStr] trait to try and parse the string literal into the
//! expected type. This is extremely useful for eg. CSS classes, letting you type
//! `class="css-class-1 css-class-2"` instead of going to the trouble of
//! constructing a [`SpacedSet<Class>`][SpacedSet]. The big caveat for this:
//! currently, the macro is not able to validate the string at compile time, and the
//! conversion will panic at runtime if the string is invalid.
//!
//! ## Example
//!
//! ```
//! # use typed_html::html;
//! # use typed_html::dom::DOMTree;
//! # use typed_html::types::{Class, SpacedSet};
//! # fn main() {
//! let classList: SpacedSet<Class> = ["foo", "bar", "baz"].into();
//! # let doc: DOMTree<String> =
//! html!(
//!     <div>
//!         <div class="foo bar baz" />         // parses a string literal
//!         <div class=["foo", "bar", "baz"] /> // uses From<[&str, &str, &str]>
//!         <div class=classList />             // uses a variable in scope
//!         <div class={                        // evaluates a code block
//!             SpacedSet::from(["foo", "bar", "baz"])
//!         } />
//!     </div>
//! )
//! # ;}
//! ```
//!
//! # Generated Nodes
//!
//! Brace blocks in the child node position are expected to return an
//! [`IntoIterator`][IntoIterator] of [`DOMTree`][DOMTree]s. You can return single
//! elements or text nodes, as they both implement `IntoIterator` for themselves.
//! The macro will consume this iterator at runtime and insert the generated nodes
//! as children in the expected position.
//!
//! ## Example
//!
//! ```
//! # use typed_html::{html, text};
//! # use typed_html::dom::DOMTree;
//! # fn main() {
//! # let doc: DOMTree<String> =
//! html!(
//!     <ul>
//!         { (1..=5).map(|i| html!(
//!             <li>{ text!("{}", i) }</li>
//!         )) }
//!     </ul>
//! )
//! # ;}
//! ```
//!
//! # Rendering
//!
//! You have two options for actually producing something useful from the DOM tree
//! that comes out of the macro.
//!
//! ## Render to a string
//!
//! The DOM tree data structure implements [`Display`][Display], so you can call
//! [`to_string()`][to_string] on it to render it to a [`String`][String]. If you
//! plan to do this, the type of the tree should be [`DOMTree<String>`][DOMTree] to
//! ensure you're not using any event handlers that can't be printed.
//!
//! ```
//! # use typed_html::html;
//! # use typed_html::dom::DOMTree;
//! # fn main() {
//! let doc: DOMTree<String> = html!(
//!     <p>"Hello Kitty"</p>
//! );
//! let doc_str = doc.to_string();
//! assert_eq!("<p>Hello Kitty</p>", doc_str);
//! # }
//! ```
//!
//! ## Render to a virtual DOM
//!
//! The DOM tree structure also implements a method called `vnode()`, which renders
//! the tree to a tree of [`VNode`][VNode]s, which is a mirror of the generated tree
//! with every attribute value rendered into `String`s. You can walk this virtual
//! DOM tree and use it to build an actual DOM tree with `stdweb` or pass it on to
//! your favourite virtual DOM system.
//!
//! # Licence
//!
//! Copyright 2018 Bodil Stokke
//!
//! This software is subject to the terms of the Mozilla Public License, v. 2.0. If
//! a copy of the MPL was not distributed with this file, You can obtain one at
//! <http://mozilla.org/MPL/2.0/>.
//!
//! # Code of Conduct
//!
//! Please note that this project is released with a [Contributor Code of
//! Conduct][coc]. By participating in this project you agree to abide by its terms.
//!
//! [coc]: https://www.contributor-covenant.org/version/1/4/code-of-conduct
//! [JSX]: https://reactjs.org/docs/introducing-jsx.html
//! [Display]: https://doc.rust-lang.org/std/fmt/trait.Display.html
//! [String]: https://doc.rust-lang.org/std/string/struct.String.html
//! [to_string]: https://doc.rust-lang.org/std/string/trait.ToString.html#tymethod.to_string
//! [Node]: dom/trait.Node.html
//! [VNode]: dom/enum.VNode.html
//! [FromStr]: https://doc.rust-lang.org/std/str/trait.FromStr.html
//! [SpacedSet]: types/struct.SpacedSet.html
//! [IntoIterator]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
//! [Into]: https://doc.rust-lang.org/std/convert/trait.Into.html
//! [Into::into]: https://doc.rust-lang.org/std/convert/trait.Into.html#method.into
//! [DOMTree]: dom/type.DOMTree.html

#[macro_use]
extern crate strum_macros;

pub extern crate htmlescape;
extern crate http;
extern crate language_tags;
extern crate mime;
extern crate proc_macro_hack;
extern crate proc_macro_nested;
extern crate stdweb;
extern crate strum;
extern crate typed_html_macros;

use proc_macro_hack::proc_macro_hack;

#[proc_macro_hack(support_nested)]
pub use typed_html_macros::html;

pub mod dom;
pub mod elements;
pub mod events;
pub mod types;

/// Marker trait for outputs
pub trait OutputType {}

/// String output
impl OutputType for String {}

/// DOM output
pub struct DOM;
impl OutputType for DOM {}