yo_html/
lib.rs

1//! JSX-like macro similar to what you can find in React or Yew but without framework nor trait.
2//!
3//! ```ignore
4//! let name = "Tippsie";
5//! let class = "class";
6//! let onclick = todo!();
7//! let dynamic_attribute = "style";
8//! html! {
9//!     <>      // Support fragments
10//!         <div class="important">{"Hello "}<strong>{name}</strong></div>
11//!         <ul class=["list", "of", class]>
12//!             <li><button {onclick}>{"Click here"}</button></li>
13//!             <li {dynamic_attribute}="color:red"></li>
14//!             <li>("%x", 42)</li>     // Shorthand for: format_args!("%x", 42)
15//!         </ul>
16//!     </>
17//! }
18//! ```
19//!
20//! An example of web framework is provided in the `examples` directory but you need to make your
21//! own for this macro to be usable.
22
23mod generate_builder;
24mod parser;
25
26#[proc_macro]
27pub fn html(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
28    if input.is_empty() {
29        return quote::quote! {
30            {
31                use html_context::*;
32
33                Fragment::builder("").finish()
34            }
35        }
36        .into();
37    }
38
39    // Parse the input tokens into a syn AST
40    let item = syn::parse_macro_input!(input as HtmlElement);
41
42    let builder = item.generate_builder();
43
44    quote::quote! {
45        {
46            use html_context::*;
47
48            #builder
49        }
50    }
51    .into()
52}
53
54enum HtmlElement {
55    Tagged(HtmlElementTag),
56    Fragmented(HtmlElementFragment),
57    Block(syn::Block),
58    Format(HtmlElementFormat),
59}
60
61#[allow(dead_code)]
62struct HtmlElementTag {
63    opening_tag: HtmlOpeningTag,
64    children: Vec<HtmlElement>,
65    closing_tag: Option<HtmlClosingTag>,
66}
67
68#[allow(dead_code)]
69struct HtmlElementFragment {
70    opening_fragment: HtmlOpeningFragment,
71    children: Vec<HtmlElement>,
72    closing_fragment: HtmlClosingFragment,
73}
74
75#[allow(dead_code)]
76struct HtmlOpeningFragment {
77    opening_bracket_token: syn::Token![<],
78    closing_bracket_token: syn::Token![>],
79}
80
81#[allow(dead_code)]
82struct HtmlClosingFragment {
83    opening_bracket_token: syn::Token![<],
84    closing_slash_token: syn::Token![/],
85    closing_bracket_token: syn::Token![>],
86}
87
88#[allow(dead_code)]
89struct HtmlOpeningTag {
90    opening_bracket_token: syn::Token![<],
91    tag: syn::Ident,
92    generics: syn::Generics,
93    attributes: Vec<HtmlAttribute>,
94    self_closing_slash_token: Option<syn::Token![/]>,
95    closing_bracket_token: syn::Token![>],
96}
97
98#[allow(dead_code)]
99struct HtmlClosingTag {
100    opening_bracket_token: syn::Token![<],
101    closing_slash_token: syn::Token![/],
102    tag: syn::Ident,
103    closing_bracket_token: syn::Token![>],
104}
105
106#[allow(dead_code)]
107struct HtmlAttribute {
108    name: HtmlAttributeName,
109    eq_token: Option<syn::Token![=]>,
110    value: Option<HtmlAttributeValue>,
111}
112
113#[allow(dead_code)]
114enum HtmlAttributeName {
115    Block(syn::Block),
116    Ident(syn::Ident),
117    Shorthand {
118        brace_token: syn::token::Brace,
119        ident: syn::Ident,
120    },
121}
122
123#[allow(dead_code)]
124enum HtmlAttributeValue {
125    Block(syn::Block),
126    ExprArray(syn::ExprArray),
127    Lit(syn::Lit),
128}
129
130#[allow(dead_code)]
131struct HtmlElementFormat {
132    paren_token: syn::token::Paren,
133    args: syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>,
134}