~repos /tide-jsx

#rust#proc-macro#jsx

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

Tide + JSX


6534ef8a Gal Schlezinger

6 years ago
Refactor proc macro to multiple structs (#1)
render_macros/src/child.rs ADDED
@@ -0,0 +1,32 @@
1
+ use crate::element::Element;
2
+ use quote::{quote, ToTokens};
3
+ use syn::parse::{Parse, ParseStream, Result};
4
+
5
+ pub enum Child {
6
+ Element(Element),
7
+ RawBlock(syn::Block),
8
+ }
9
+
10
+ impl ToTokens for Child {
11
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
12
+ match self {
13
+ Self::Element(element) => element.to_tokens(tokens),
14
+ Self::RawBlock(block) => {
15
+ let ts = quote! { #block };
16
+ ts.to_tokens(tokens);
17
+ }
18
+ }
19
+ }
20
+ }
21
+
22
+ impl Parse for Child {
23
+ fn parse(input: ParseStream) -> Result<Self> {
24
+ match input.parse::<Element>() {
25
+ Ok(element) => Ok(Self::Element(element)),
26
+ Err(_) => {
27
+ let block = input.parse::<syn::Block>()?;
28
+ Ok(Self::RawBlock(block))
29
+ }
30
+ }
31
+ }
32
+ }
render_macros/src/children.rs ADDED
@@ -0,0 +1,63 @@
1
+ use crate::child::Child;
2
+ use quote::{quote, ToTokens};
3
+ use syn::parse::{Parse, ParseStream, Result};
4
+
5
+ #[derive(Default)]
6
+ pub struct Children {
7
+ pub nodes: Vec<Child>,
8
+ }
9
+
10
+ impl Children {
11
+ pub fn new(nodes: Vec<Child>) -> Self {
12
+ Children { nodes }
13
+ }
14
+
15
+ pub fn len(&self) -> usize {
16
+ self.nodes.len()
17
+ }
18
+
19
+ pub fn as_option_of_tuples_tokens(&self) -> proc_macro2::TokenStream {
20
+ let children_quotes: Vec<_> = self
21
+ .nodes
22
+ .iter()
23
+ .map(|child| {
24
+ quote! { #child }
25
+ })
26
+ .collect();
27
+ match children_quotes.len() {
28
+ 0 => quote! { Option::<()>::None },
29
+ 1 => quote! { Some(#(#children_quotes)*) },
30
+ _ => {
31
+ let mut iter = children_quotes.iter();
32
+ let first = iter.next().unwrap();
33
+ let second = iter.next().unwrap();
34
+ let tuple_of_tuples = iter.fold(
35
+ quote!((#first, #second)),
36
+ |renderable, current| quote!((#current, #renderable)),
37
+ );
38
+
39
+ quote! { Some(#tuple_of_tuples) }
40
+ }
41
+ }
42
+ }
43
+ }
44
+
45
+ impl Parse for Children {
46
+ fn parse(input: ParseStream) -> Result<Self> {
47
+ let mut nodes = vec![];
48
+
49
+ while !input.peek(syn::Token![<]) || !input.peek2(syn::Token![/]) {
50
+ if let Ok(child) = input.parse::<Child>() {
51
+ nodes.push(child);
52
+ }
53
+ }
54
+
55
+ Ok(Self::new(nodes))
56
+ }
57
+ }
58
+
59
+ impl ToTokens for Children {
60
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
61
+ self.as_option_of_tuples_tokens().to_tokens(tokens);
62
+ }
63
+ }
render_macros/src/element.rs ADDED
@@ -0,0 +1,63 @@
1
+ use crate::children::Children;
2
+ use crate::element_attributes::ElementAttributes;
3
+ use crate::tags::{ClosingTag, OpenTag};
4
+ use quote::{quote, ToTokens};
5
+ use syn::parse::{Parse, ParseStream, Result};
6
+
7
+ pub struct Element {
8
+ name: syn::Ident,
9
+ attributes: ElementAttributes,
10
+ children: Children,
11
+ }
12
+
13
+ impl Parse for Element {
14
+ fn parse(input: ParseStream) -> Result<Self> {
15
+ let open_tag = input.parse::<OpenTag>()?;
16
+
17
+ let children = if open_tag.self_closing {
18
+ Children::default()
19
+ } else {
20
+ let children = input.parse::<Children>()?;
21
+ let closing_tag = input.parse::<ClosingTag>()?;
22
+ closing_tag.validate(&open_tag);
23
+ children
24
+ };
25
+
26
+ Ok(Element {
27
+ name: open_tag.name,
28
+ attributes: open_tag.attributes,
29
+ children,
30
+ })
31
+ }
32
+ }
33
+
34
+ impl Element {
35
+ pub fn is_custom_element(&self) -> bool {
36
+ let name = self.name.to_string();
37
+ let first_letter = name.get(0..1).unwrap();
38
+ first_letter.to_uppercase() == first_letter
39
+ }
40
+ }
41
+
42
+ impl ToTokens for Element {
43
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
44
+ let name = &self.name;
45
+
46
+ let declaration = if self.is_custom_element() {
47
+ let attrs = self.attributes.for_custom_element(&self.children);
48
+ quote! { #name #attrs }
49
+ } else {
50
+ let attrs = self.attributes.for_simple_element();
51
+ let children_tuple = self.children.as_option_of_tuples_tokens();
52
+ quote! {
53
+ ::render::SimpleElement {
54
+ tag_name: stringify!(#name),
55
+ attributes: #attrs,
56
+ contents: #children_tuple,
57
+ }
58
+ }
59
+ };
60
+
61
+ declaration.to_tokens(tokens);
62
+ }
63
+ }
render_macros/src/element_attributes.rs ADDED
@@ -0,0 +1,127 @@
1
+ use crate::children::Children;
2
+ use crate::element_attribute::ElementAttribute;
3
+ use quote::{quote, ToTokens};
4
+ use std::collections::HashSet;
5
+ use syn::parse::{Parse, ParseStream, Result};
6
+
7
+ pub type Attributes = HashSet<ElementAttribute>;
8
+
9
+ pub struct ElementAttributes {
10
+ pub attributes: Attributes,
11
+ }
12
+
13
+ impl ElementAttributes {
14
+ pub fn new(attributes: Attributes) -> Self {
15
+ Self { attributes }
16
+ }
17
+
18
+ pub fn for_custom_element<'c>(
19
+ &self,
20
+ children: &'c Children,
21
+ ) -> CustomElementAttributes<'_, 'c> {
22
+ CustomElementAttributes {
23
+ attributes: &self.attributes,
24
+ children,
25
+ }
26
+ }
27
+
28
+ pub fn for_simple_element(&self) -> SimpleElementAttributes<'_> {
29
+ SimpleElementAttributes {
30
+ attributes: &self.attributes,
31
+ }
32
+ }
33
+ }
34
+
35
+ impl Parse for ElementAttributes {
36
+ fn parse(input: ParseStream) -> Result<Self> {
37
+ let mut attributes: HashSet<ElementAttribute> = HashSet::new();
38
+ while input.peek(syn::Ident) {
39
+ if let Ok(attribute) = input.parse::<ElementAttribute>() {
40
+ if attributes.contains(&attribute) {
41
+ let error_message = format!(
42
+ "There is a previous definition of the {} attribute",
43
+ attribute.ident()
44
+ );
45
+ attribute
46
+ .ident()
47
+ .span()
48
+ .unwrap()
49
+ .warning(error_message)
50
+ .emit();
51
+ }
52
+ attributes.insert(attribute);
53
+ }
54
+ }
55
+ Ok(ElementAttributes::new(attributes))
56
+ }
57
+ }
58
+
59
+ pub struct CustomElementAttributes<'a, 'c> {
60
+ attributes: &'a Attributes,
61
+ children: &'c Children,
62
+ }
63
+
64
+ impl<'a, 'c> ToTokens for CustomElementAttributes<'a, 'c> {
65
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
66
+ let mut attrs: Vec<_> = self
67
+ .attributes
68
+ .iter()
69
+ .map(|attribute| {
70
+ let ident = attribute.ident();
71
+ let value = attribute.value_tokens();
72
+
73
+ quote! {
74
+ #ident: #value
75
+ }
76
+ })
77
+ .collect();
78
+
79
+ if self.children.len() > 0 {
80
+ let children_tuple = self.children.as_option_of_tuples_tokens();
81
+ attrs.push(quote! {
82
+ children: #children_tuple
83
+ });
84
+ }
85
+
86
+ let quoted = if attrs.len() == 0 {
87
+ quote!()
88
+ } else {
89
+ quote!({ #(#attrs),* })
90
+ };
91
+
92
+ quoted.to_tokens(tokens);
93
+ }
94
+ }
95
+
96
+ pub struct SimpleElementAttributes<'a> {
97
+ attributes: &'a Attributes,
98
+ }
99
+
100
+ impl<'a> ToTokens for SimpleElementAttributes<'a> {
101
+ fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
102
+ if self.attributes.is_empty() {
103
+ quote!(None).to_tokens(tokens);
104
+ } else {
105
+ let attrs: Vec<_> = self
106
+ .attributes
107
+ .iter()
108
+ .map(|attribute| {
109
+ let ident = attribute.ident();
110
+ let value = attribute.value_tokens();
111
+
112
+ quote! {
113
+ hm.insert(stringify!(#ident), #value);
114
+ }
115
+ })
116
+ .collect();
117
+
118
+ let hashmap_declaration = quote! {{
119
+ let mut hm = std::collections::HashMap::<&str, &str>::new();
120
+ #(#attrs)*
121
+ Some(hm)
122
+ }};
123
+
124
+ hashmap_declaration.to_tokens(tokens);
125
+ }
126
+ }
127
+ }
render_macros/src/lib.rs CHANGED
@@ -1,208 +1,19 @@
1
1
  #![feature(proc_macro_diagnostic, proc_macro_hygiene)]
2
2
 
3
- // TODO: Extract a `Children` struct that can implement `Parse` and `ToTokens`:
4
- // - `Parse` will do the nasty things inside `Element`
5
- // - `ToTokens` will do the trick with the tuples
6
-
7
3
  extern crate proc_macro;
8
4
 
5
+ mod child;
6
+ mod children;
7
+ mod element;
9
8
  mod element_attribute;
9
+ mod element_attributes;
10
+ mod tags;
10
11
 
11
- use element_attribute::ElementAttribute;
12
+ use element::Element;
12
13
  use proc_macro::TokenStream;
13
- use quote::{quote, ToTokens};
14
+ use quote::quote;
14
- use std::collections::HashSet;
15
- use syn::parse::{Parse, ParseStream, Result};
16
15
  use syn::parse_macro_input;
17
16
 
18
- struct Element {
19
- name: syn::Ident,
20
- attributes: HashSet<ElementAttribute>,
21
- children: Vec<RenderableItem>,
22
- }
23
-
24
- enum RenderableItem {
25
- Element(Element),
26
- RawBlock(syn::Block),
27
- }
28
-
29
- impl ToTokens for RenderableItem {
30
- fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
31
- match self {
32
- Self::Element(element) => element.to_tokens(tokens),
33
- Self::RawBlock(block) => {
34
- let ts = quote! { #block };
35
- ts.to_tokens(tokens);
36
- }
37
- }
38
- }
39
- }
40
-
41
- impl Parse for RenderableItem {
42
- fn parse(input: ParseStream) -> Result<Self> {
43
- match input.parse::<Element>() {
44
- Ok(element) => Ok(Self::Element(element)),
45
- Err(_) => {
46
- let block = input.parse::<syn::Block>()?;
47
- Ok(Self::RawBlock(block))
48
- }
49
- }
50
- }
51
- }
52
-
53
- impl Parse for Element {
54
- fn parse(input: ParseStream) -> Result<Self> {
55
- let mut attributes: HashSet<ElementAttribute> = HashSet::new();
56
- let _starts_a_tag = input.parse::<syn::Token![<]>().is_ok();
57
-
58
- let name = input.parse()?;
59
-
60
- while input.peek(syn::Ident) {
61
- if let Ok(attribute) = input.parse::<ElementAttribute>() {
62
- if attributes.contains(&attribute) {
63
- let error_message = format!(
64
- "There is a previous definition of the {} attribute",
65
- attribute.ident()
66
- );
67
- attribute
68
- .ident()
69
- .span()
70
- .unwrap()
71
- .warning(error_message)
72
- .emit();
73
- }
74
- attributes.insert(attribute);
75
- }
76
- }
77
-
78
- let can_have_contents = input.parse::<syn::Token![/]>().is_err();
79
- input.parse::<syn::Token![>]>()?;
80
-
81
- let mut children = vec![];
82
-
83
- if can_have_contents {
84
- while !input.peek(syn::Token![<]) || !input.peek2(syn::Token![/]) {
85
- if let Ok(child) = input.parse::<RenderableItem>() {
86
- children.push(child);
87
- }
88
- }
89
-
90
- // parse closing
91
- input.parse::<syn::Token![<]>()?;
92
- input.parse::<syn::Token![/]>()?;
93
- let closing_name: syn::Ident = input.parse()?;
94
- if closing_name != name {
95
- let error_message = format!("Expected closing tag for: <{}>", &name);
96
- closing_name.span().unwrap().error(error_message).emit();
97
- }
98
- input.parse::<syn::Token![>]>()?;
99
- }
100
-
101
- Ok(Element {
102
- name,
103
- attributes,
104
- children,
105
- })
106
- }
107
- }
108
-
109
- impl Element {
110
- pub fn is_custom_element(&self) -> bool {
111
- let name = self.name.to_string();
112
- let first_letter = name.get(0..1).unwrap();
113
- first_letter.to_uppercase() == first_letter
114
- }
115
- }
116
-
117
- impl ToTokens for Element {
118
- fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
119
- let Element { name, children, .. } = self;
120
-
121
- let children_quotes: Vec<_> = children
122
- .iter()
123
- .map(|child| {
124
- quote! { #child }
125
- })
126
- .collect();
127
- let children_tuple = match children_quotes.len() {
128
- 0 => quote! { Option::<()>::None },
129
- 1 => quote! { Some(#(#children_quotes)*) },
130
- _ => {
131
- let mut iter = children_quotes.iter();
132
- let first = iter.next().unwrap();
133
- let second = iter.next().unwrap();
134
- let tuple_of_tuples = iter.fold(
135
- quote!((#first, #second)),
136
- |renderable, current| quote!((#current, #renderable)),
137
- );
138
-
139
- quote! { Some(#tuple_of_tuples) }
140
- }
141
- };
142
-
143
- let declaration = if self.is_custom_element() {
144
- let mut attrs: Vec<_> = self
145
- .attributes
146
- .iter()
147
- .map(|attribute| {
148
- let ident = attribute.ident();
149
- let value = attribute.value_tokens();
150
-
151
- quote! {
152
- #ident: #value
153
- }
154
- })
155
- .collect();
156
-
157
- if children_quotes.len() > 0 {
158
- attrs.push(quote! {
159
- children: #children_tuple
160
- });
161
- }
162
-
163
- if attrs.len() == 0 {
164
- quote! { #name }
165
- } else {
166
- quote! {
167
- #name {
168
- #(#attrs),*
169
- }
170
- }
171
- }
172
- } else {
173
- let attrs: Vec<_> = self
174
- .attributes
175
- .iter()
176
- .map(|attribute| {
177
- let ident = attribute.ident();
178
- let value = attribute.value_tokens();
179
-
180
- quote! {
181
- hm.insert(stringify!(#ident), #value);
182
- }
183
- })
184
- .collect();
185
- let attributes_value = if self.attributes.len() == 0 {
186
- quote!(None)
187
- } else {
188
- quote! {{
189
- let mut hm = std::collections::HashMap::<&str, &str>::new();
190
- #(#attrs)*
191
- Some(hm)
192
- }}
193
- };
194
- quote! {
195
- ::render::SimpleElement {
196
- tag_name: stringify!(#name),
197
- attributes: #attributes_value,
198
- contents: #children_tuple,
199
- }
200
- }
201
- };
202
- declaration.to_tokens(tokens);
203
- }
204
- }
205
-
206
17
  /// Render a component tree to an HTML string
207
18
  #[proc_macro]
208
19
  pub fn html(input: TokenStream) -> TokenStream {
render_macros/src/tags.rs ADDED
@@ -0,0 +1,47 @@
1
+ use crate::element_attributes::ElementAttributes;
2
+ use syn::parse::{Parse, ParseStream, Result};
3
+
4
+ pub struct OpenTag {
5
+ pub name: syn::Ident,
6
+ pub attributes: ElementAttributes,
7
+ pub self_closing: bool,
8
+ }
9
+
10
+ impl Parse for OpenTag {
11
+ fn parse(input: ParseStream) -> Result<Self> {
12
+ let _starts_a_tag = input.parse::<syn::Token![<]>().is_ok();
13
+ let name = input.parse()?;
14
+ let attributes = input.parse::<ElementAttributes>()?;
15
+ let self_closing = input.parse::<syn::Token![/]>().is_ok();
16
+ input.parse::<syn::Token![>]>()?;
17
+
18
+ Ok(Self {
19
+ name,
20
+ attributes,
21
+ self_closing,
22
+ })
23
+ }
24
+ }
25
+
26
+ pub struct ClosingTag {
27
+ name: syn::Ident,
28
+ }
29
+
30
+ impl ClosingTag {
31
+ pub fn validate(&self, open_tag: &OpenTag) {
32
+ if self.name != open_tag.name {
33
+ let error_message = format!("Expected closing tag for: <{}>", &open_tag.name);
34
+ self.name.span().unwrap().error(error_message).emit();
35
+ }
36
+ }
37
+ }
38
+
39
+ impl Parse for ClosingTag {
40
+ fn parse(input: ParseStream) -> Result<Self> {
41
+ input.parse::<syn::Token![<]>()?;
42
+ input.parse::<syn::Token![/]>()?;
43
+ let name = input.parse::<syn::Ident>()?;
44
+ input.parse::<syn::Token![>]>()?;
45
+ Ok(Self { name })
46
+ }
47
+ }