ayyeve_ui_proc_macros/
lib.rs1use proc_macro::TokenStream;
2use quote::*;
3use syn::*;
4
5#[proc_macro_derive(Dropdown, attributes(Dropdown))]
11pub fn dropdown(input: TokenStream) -> TokenStream {
12 let ast:DeriveInput = syn::parse(input).unwrap();
14 let mut entries = Vec::new();
15 let mut debug = false;
16
17 struct DropdownVariant {
18 display: String,
20 name: String,
22 }
23
24
25 for attr in &ast.attrs {
26 if !attr.path.is_ident("Dropdown") {continue}
27
28 if let Ok(Meta::List(list)) = attr.parse_meta() {
29 for i in list.nested {
30 if let NestedMeta::Meta(Meta::Path(p)) = &i {
31 if p.is_ident("debug") {
32 debug = true;
33 } else {
34 panic!("Unknown enum attribute {}", p.get_ident().unwrap())
35 }
36 }
37 }
38 }
39 }
40
41
42 if let Data::Enum(data) = &ast.data {
44 for v in data.variants.iter() {
45 let mut variant = DropdownVariant {display: v.ident.to_string(), name: v.ident.to_string()};
46 let mut ignore = false;
47
48 for a in v.attrs.iter() {
50 if !a.path.is_ident("Dropdown") {continue}
51
52 if let Ok(Meta::List(list)) = a.parse_meta() {
53 for i in list.nested {
54 if let NestedMeta::Meta(Meta::NameValue(name_value)) = &i {
55 if let Lit::Str(display) = &name_value.lit {
57 variant.display = display.value()
58 }
59 }
60
61 if let NestedMeta::Meta(Meta::Path(name)) = &i {
63 if name.is_ident("ignore") {
64 ignore = true;
65 break
66 }
67 }
68
69 }
70 }
71 }
72
73 if ignore {continue}
75
76 entries.push(variant)
78 }
79 }
80
81 if entries.is_empty() {panic!("enum is empty or all variants are ignored")}
83
84 let enum_name = ast.ident.to_string();
85 let mut to_string = String::new();
86 let mut from_string = String::new();
87 let mut variants = String::new();
88
89 let t = " ";
90
91 for variant in entries {
92 to_string += &format!("{t}{t}{t}{t}Self::{} => \"{}\".to_owned(), \n", variant.name, variant.display);
93 from_string += &format!("{t}{t}{t}{t}\"{}\" => Self::{}, \n", variant.display, variant.name);
94 variants += &format!("{t}{t}{t}{t}Self::{},\n", variant.name);
95 }
96 let variants = variants.trim();
97 let to_string = to_string.trim();
98 let from_string = from_string.trim();
99
100 let gen = format!(r#"
101 impl Dropdownable for {enum_name} {{
102 fn variants() -> Vec<Self> {{
103 vec![
104 {variants}
105 ]
106 }}
107 fn display_text(&self) -> String {{
108 match self {{
109 {to_string}
110 other => panic!("variant {{:?}} was ignored", other)
111 }}
112 }}
113 fn from_string(string: String) -> Self {{
114 match &*string {{
115 {from_string}
116 other => panic!("variant does not exist for enum {enum_name} and string {{}}", other)
117 }}
118 }}
119 }}"#);
120
121 if debug {println!("generated: \n{}", gen)}
122
123 let gen = gen.parse::<proc_macro2::TokenStream>().unwrap();
125 proc_macro::TokenStream::from(quote!{#gen})
126}
127
128
129#[proc_macro_derive(ScrollableGettersSetters, attributes(Scrollable))]
133pub fn scrollable_getters_setters(input: TokenStream) -> TokenStream {
134 let ast:DeriveInput = syn::parse(input).unwrap();
135
136 let mut has_pos = false;
137 let mut has_tag = false;
138 let mut has_hover = false;
139 let mut has_selected = false;
140 let mut has_size = false;
141
142 let mut selectable = true;
143 let mut multi_selectable = false;
144
145 let mut generics = String::new();
146
147 for attr in &ast.attrs {
148 if !attr.path.is_ident("Scrollable") {continue}
149
150 if let Ok(Meta::List(list)) = attr.parse_meta() {
151 for i in list.nested {
152 if let NestedMeta::Meta(Meta::NameValue(name_value)) = &i {
153 if let Lit::Bool(i) = &name_value.lit {
154 if name_value.path.is_ident("selectable") {
155 selectable = i.value()
156 } else if name_value.path.is_ident("multi_selectable") {
157 multi_selectable = i.value()
158 } else {
159 panic!("unknown attribute {}", name_value.path.get_ident().unwrap())
160 }
161 } else if let Lit::Str(i) = &name_value.lit {
162 if name_value.path.is_ident("generics") {
163 generics = i.value()
164 } else {
165 panic!("unknown attribute {}", name_value.path.get_ident().unwrap())
166 }
167 }
168 }
169 }
170 }
171 }
172
173 if let Data::Struct(s) = &ast.data {
174 for f in &s.fields {
175 if let Some(ident) = &f.ident {
176 has_size |= ident.to_string() == "size";
177 has_pos |= ident.to_string() == "pos";
178 has_tag |= ident.to_string() == "tag";
179 has_hover |= ident.to_string() == "hover";
180 has_selected |= ident.to_string() == "selected";
181 }
182 if has_pos && has_tag && has_hover && has_selected {break}
183 }
184 }
185 if !has_size {panic!("Scrollable does not have a size field")}
186
187 let generics = {
188 if generics.len() > 0 {format!("<{generics}>")}
189 else {String::new()}
190 };
191 let generics2 = generics.split(",").map(|g|g.split(":").next().unwrap()).collect::<Vec<_>>().join(",") + if generics.len() > 0 {">"} else {""};
192
193 let struct_name = ast.ident.to_string();
194
195 let mut str = String::new();
196 str += &format!("impl{generics} ScrollableItemGettersSetters for {struct_name}{generics2} {{\n");
197
198 str += " fn size(&self) -> Vector2 {self.size}\n fn set_size(&mut self, new_size: Vector2) {self.size = new_size}\n";
200
201 if has_pos {
202 str += " fn get_pos(&self) -> Vector2 {self.pos}\n fn set_pos(&mut self, pos:Vector2) {self.pos = pos}\n"
203 }
204 if has_tag {
205 str += " fn get_tag(&self) -> String {self.tag.clone()}\n fn set_tag(&mut self, tag:&str) {self.tag = tag.to_owned()}\n"
206 }
207 if has_hover {
208 str += " fn get_hover(&self) -> bool {self.hover}\n fn set_hover(&mut self, hover:bool) {self.hover = hover}\n"
209 }
210 if has_selected {
211 str += " fn get_selected(&self) -> bool {self.selected}\n fn set_selected(&mut self, selected:bool) {self.selected = selected}\n";
212 }
213
214 str += &format!(" fn get_selectable(&self) -> bool {{{selectable}}}\n fn get_multi_selectable(&self) -> bool {{{multi_selectable}}}\n");
215
216 str += "}";
217
218
219 let tokens = str.parse::<proc_macro2::TokenStream>().unwrap();
220 proc_macro::TokenStream::from(quote! {#tokens})
221}
222