Skip to main content

viewy_codegen/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn;
4use syn::{LitStr, Meta};
5
6#[proc_macro_derive(Widget, attributes(widget))]
7pub fn widget_derive(input: TokenStream) -> TokenStream {
8    // Parse the input tokens into a syntax tree
9    let input = syn::parse_macro_input!(input as syn::DeriveInput);
10    // Extract name of the struct
11    let name = &input.ident;
12
13    // Parse `#[widget(script = "./script.js", style = "style.scss")]` attribute, if present
14    let mut style_value: Option<String> = None;
15    let mut script_value: Option<String> = None;
16
17    for option in input.attrs.into_iter() {
18        if let Meta::List(ref list) = option.meta {
19            if list.path.is_ident("widget") {
20                list.parse_nested_meta(|meta| {
21                    if meta.path.is_ident("style") {
22                        let value = meta.value()?;
23                        let s: LitStr = value.parse()?;
24                        style_value = Some(s.value());
25                    }
26                    if meta.path.is_ident("script") {
27                        let value = meta.value()?;
28                        let s: LitStr = value.parse()?;
29                        script_value = Some(s.value());
30                    }
31                    Ok(())
32                })
33                    .expect("Can't parse attribute widget, check syntax");
34            }
35        }
36    }
37
38    let style_str = style_value.expect("style is a mandatory attribute in widget macro");
39
40    let generated_code = quote! {
41        use std::ops::{Deref, DerefMut};
42
43
44        impl Into<Node> for #name {
45            fn into(self) -> Node {
46                let mut widget = self;
47                widget.render();
48                widget.node
49            }
50        }
51       impl Into<Node> for &mut #name {
52            fn into(self) -> Node {
53                let widget = self;
54                widget.render();
55                widget.node.clone()
56            }
57        }
58
59        impl Deref for #name {
60            type Target = Node;
61
62            fn deref(&self) -> &Self::Target {
63                &self.node
64            }
65        }
66        impl DerefMut for #name {
67            fn deref_mut(&mut self) -> &mut Self::Target {
68                &mut self.node
69            }
70        }
71
72        impl Widget for #name {
73            const STYLE: &'static str = include_str!(#style_str);
74            fn widget_name() -> &'static str {
75                "#name"
76            }
77
78        }
79    };
80    generated_code.into()
81}
82
83#[proc_macro_derive(Component)]
84pub fn component_derive(input: TokenStream) -> TokenStream {
85    // Construct a representation of Rust code as a syntax tree
86    // that we can manipulate
87    let ast = syn::parse(input).unwrap();
88
89    // Build the trait implementation
90    impl_component_macro(&ast)
91}
92
93fn impl_component_macro(ast: &syn::DeriveInput) -> TokenStream {
94    let name = &ast.ident;
95    let generated_code = quote! {
96       impl Into<Node> for #name {
97            fn into(self) -> Node {
98                self.render()
99            }
100        }
101    };
102    generated_code.into()
103}
104
105#[proc_macro_derive(Appendable)]
106pub fn appendable_derive(input: TokenStream) -> TokenStream {
107    // Construct a representation of Rust code as a syntax tree
108    // that we can manipulate
109    let ast = syn::parse(input).unwrap();
110
111    // Build the trait implementation
112    impl_appendable_macro(&ast)
113}
114
115fn impl_appendable_macro(ast: &syn::DeriveInput) -> TokenStream {
116    let name = &ast.ident;
117    let generated_code = quote! {
118        impl Appendable for #name {}
119    };
120    generated_code.into()
121}
122
123#[proc_macro_derive(Attributable)]
124pub fn attributable_derive(input: TokenStream) -> TokenStream {
125    // Construct a representation of Rust code as a syntax tree
126    // that we can manipulate
127    let ast = syn::parse(input).unwrap();
128
129    // Build the trait implementation
130    impl_attributable_macro(&ast)
131}
132
133fn impl_attributable_macro(ast: &syn::DeriveInput) -> TokenStream {
134    let name = &ast.ident;
135    let generated_code = quote! {
136        impl Attributable for #name {}
137    };
138    generated_code.into()
139}
140
141#[proc_macro_derive(Classable)]
142pub fn classable_derive(input: TokenStream) -> TokenStream {
143    // Construct a representation of Rust code as a syntax tree
144    // that we can manipulate
145    let ast = syn::parse(input).unwrap();
146
147    // Build the trait implementation
148    impl_classable_macro(&ast)
149}
150
151fn impl_classable_macro(ast: &syn::DeriveInput) -> TokenStream {
152    let name = &ast.ident;
153    let generated_code = quote! {
154        impl Classable for #name {}
155    };
156    generated_code.into()
157}
158#[proc_macro_derive(Colorable)]
159pub fn colorable_derive(input: TokenStream) -> TokenStream {
160    // Construct a representation of Rust code as a syntax tree
161    // that we can manipulate
162    let ast = syn::parse(input).unwrap();
163
164    // Build the trait implementation
165    impl_colorable_macro(&ast)
166}
167
168fn impl_colorable_macro(ast: &syn::DeriveInput) -> TokenStream {
169    let name = &ast.ident;
170    let generated_code = quote! {
171        impl Colorable for #name {}
172    };
173    generated_code.into()
174}
175
176#[proc_macro_derive(Dimensionable)]
177pub fn dimensionable_derive(input: TokenStream) -> TokenStream {
178    // Construct a representation of Rust code as a syntax tree
179    // that we can manipulate
180    let ast = syn::parse(input).unwrap();
181
182    // Build the trait implementation
183    impl_dimensionable_macro(&ast)
184}
185
186fn impl_dimensionable_macro(ast: &syn::DeriveInput) -> TokenStream {
187    let name = &ast.ident;
188    let generated_code = quote! {
189        impl Dimensionable for #name {}
190    };
191    generated_code.into()
192}
193
194#[proc_macro_derive(Cardifiable)]
195pub fn cardifiable_derive(input: TokenStream) -> TokenStream {
196    // Construct a representation of Rust code as a syntax tree
197    // that we can manipulate
198    let ast = syn::parse(input).unwrap();
199
200    // Build the trait implementation
201    impl_cardifiable_macro(&ast)
202}
203
204fn impl_cardifiable_macro(ast: &syn::DeriveInput) -> TokenStream {
205    let name = &ast.ident;
206    let generated_code = quote! {
207        impl Cardifiable for #name {}
208    };
209    generated_code.into()
210}