Skip to main content

xrune_incant/
lib.rs

1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use syn::parse_macro_input;
5
6use xrune_nexus::ds_node::DsRoot;
7use xrune_nexus::ds_rune::DsRune;
8use xrune_nexus::ds_rune::decipher::decipher;
9
10/// Default rune: generates println debug output (xrune style).
11struct DefaultRune {
12    tokens: proc_macro2::TokenStream,
13    parent_name: String,
14}
15
16impl DefaultRune {
17    fn new() -> Self {
18        Self {
19            tokens: proc_macro2::TokenStream::new(),
20            parent_name: String::new(),
21        }
22    }
23}
24
25impl DsRune for DefaultRune {
26    fn inscribe_root(&mut self, parent_expr: &syn::Expr) {
27        use quote::quote;
28        self.tokens.extend(quote! {
29            println!("let parent = {:?}", #parent_expr);
30        });
31        self.parent_name = "parent".to_string();
32    }
33
34    fn inscribe_widget(
35        &mut self,
36        name: &syn::Ident,
37        attrs: &[xrune_nexus::ds_node::ds_attr::DsAttr],
38        _enchants: &[syn::Expr],
39        on_handlers: &[xrune_nexus::ds_node::ds_on::DsOn],
40        children: &[xrune_nexus::ds_node::DsTreeRef],
41    ) {
42        use quote::quote;
43        let name_string = name.to_string();
44        let parent_name = &self.parent_name;
45
46        self.tokens.extend(quote! {
47            println!("let {} = obj::new({})", #name_string, #parent_name);
48        });
49
50        for attr in attrs {
51            let attr_name = match &attr.name {
52                Some(n) => n.to_string(),
53                None => "<positional>".to_string(),
54            };
55            let attr_value = &attr.value;
56            self.tokens.extend(quote! {
57                println!("{}.set_{}({:?})", #name_string, #attr_name, #attr_value);
58            });
59        }
60
61        for on in on_handlers {
62            let event_name = on.get_name().to_string();
63            let arg_count = on.get_args().len();
64            self.tokens.extend(quote! {
65                println!("{}.on_{}({} args)", #name_string, #event_name, #arg_count);
66            });
67        }
68
69        let prev_parent = self.parent_name.clone();
70        self.parent_name = name_string;
71        for child in children {
72            decipher(child, self);
73        }
74        self.parent_name = prev_parent;
75    }
76
77    fn inscribe_if(
78        &mut self,
79        condition: &syn::Expr,
80        _reactive: bool,
81        children: &[xrune_nexus::ds_node::DsTreeRef],
82        else_branch: Option<&xrune_nexus::ds_node::DsTreeRef>,
83    ) {
84        use quote::quote;
85        let con = quote!(#condition).to_string();
86        self.tokens.extend(quote! {
87            println!("if {} {{", #con);
88        });
89        for child in children {
90            decipher(child, self);
91        }
92        self.tokens.extend(quote! {
93            println!("}}");
94        });
95        if let Some(branch) = else_branch {
96            decipher(branch, self);
97        }
98    }
99
100    fn inscribe_iter(
101        &mut self,
102        iterable: &syn::Expr,
103        variable: &syn::Ident,
104        _reactive: bool,
105        _key: Option<&syn::Expr>,
106        children: &[xrune_nexus::ds_node::DsTreeRef],
107    ) {
108        use quote::quote;
109        let iterable_str = quote!(#iterable).to_string();
110        let variable_str = variable.to_string();
111        self.tokens.extend(quote! {
112            println!("for {} in {} {{", #variable_str, #iterable_str);
113        });
114        for child in children {
115            decipher(child, self);
116        }
117        self.tokens.extend(quote! {
118            println!("}}");
119        });
120    }
121
122    fn inscribe_niche(&mut self, name: &syn::Ident, children: &[xrune_nexus::ds_node::DsTreeRef]) {
123        use quote::quote;
124        let name_str = name.to_string();
125        self.tokens.extend(quote! {
126            println!("@{} {{", #name_str);
127        });
128        for child in children {
129            decipher(child, self);
130        }
131        self.tokens.extend(quote! {
132            println!("}}");
133        });
134    }
135
136    fn inscribe_match(
137        &mut self,
138        scrutinee: &syn::Expr,
139        _reactive: bool,
140        arms: &[xrune_nexus::ds_node::ds_match::DsMatchArm],
141    ) {
142        use quote::quote;
143        let scrutinee_str = quote!(#scrutinee).to_string();
144        self.tokens.extend(quote! {
145            println!("match {} {{", #scrutinee_str);
146        });
147        for arm in arms {
148            let pat = arm.get_pat();
149            let pat_str = quote!(#pat).to_string();
150            self.tokens.extend(quote! {
151                println!("  {} => {{", #pat_str);
152            });
153            for child in arm.get_children() {
154                decipher(child, self);
155            }
156            self.tokens.extend(quote! {
157                println!("  }}");
158            });
159        }
160        self.tokens.extend(quote! {
161            println!("}}");
162        });
163    }
164
165    fn seal(self) -> proc_macro2::TokenStream {
166        self.tokens
167    }
168}
169
170#[proc_macro]
171pub fn ui(input: TokenStream) -> TokenStream {
172    let root = parse_macro_input!(input as DsRoot);
173
174    let mut rune = DefaultRune::new();
175
176    // Inscribe root
177    rune.inscribe_root(&root.get_parent());
178
179    // Traverse the content tree
180    let content = root.get_content();
181    decipher(&content, &mut rune);
182
183    TokenStream::from(rune.seal())
184}