1mod enums;
2mod structs;
3
4use enums::derive_enum;
5use proc_macro::TokenStream;
6use proc_macro2::{Ident, Span};
7use quote::{ToTokens, quote};
8use structs::derive_struct;
9use syn::{Attribute, Data, DeriveInput, Error, Lit, Meta, NestedMeta, parse_macro_input};
10
11macro_rules! derive_error {
12 ($string: tt) => {
13 Error::new(Span::call_site(), $string)
14 .to_compile_error()
15 .into()
16 };
17}
18
19#[proc_macro_derive(Gui, attributes(enum2egui))]
20pub fn derive_gui(input: TokenStream) -> TokenStream {
21 let input = parse_macro_input!(input as DeriveInput);
22 let name = &input.ident;
23
24 match &input.data {
25 Data::Struct(data) => derive_struct(name, data),
26 Data::Enum(data) => derive_enum(name, data),
27 Data::Union(..) => derive_error!("enum2egui does not support unions"),
28 }
29}
30
31pub(crate) fn derive_trait(
32 name: &Ident,
33 gui: proc_macro2::TokenStream,
34 gui_mut: proc_macro2::TokenStream,
35) -> TokenStream {
36 quote! {
37 impl enum2egui::GuiInspect for #name {
38 fn ui(&self, ui: &mut egui::Ui) {
39 #gui
40 }
41
42 fn ui_mut(&mut self, ui: &mut egui::Ui) {
43 #gui_mut
44 }
45 }
46 }
47 .to_token_stream()
48 .into()
49}
50
51pub(crate) fn has_skip_attr(attrs: &[Attribute]) -> bool {
52 attrs.iter().any(is_enum2egui_skip_attr)
53}
54
55fn is_enum2egui_skip_attr(attr: &Attribute) -> bool {
56 if let Ok(Meta::List(meta_list)) = attr.parse_meta()
57 && meta_list.path.is_ident("enum2egui") {
58 return has_skip_path(&meta_list.nested);
59 }
60 false
61}
62
63fn has_skip_path(nested: &syn::punctuated::Punctuated<NestedMeta, syn::token::Comma>) -> bool {
64 nested.iter().any(|nested| {
65 if let NestedMeta::Meta(Meta::Path(path)) = nested {
66 return path.is_ident("skip");
67 }
68 false
69 })
70}
71
72pub(crate) fn get_custom_label(attrs: &Vec<syn::Attribute>) -> Option<String> {
73 for attr in attrs {
74 if attr.path.is_ident("enum2egui")
75 && let Ok(syn::Meta::List(meta_list)) = attr.parse_meta() {
76 for nested_meta in meta_list.nested {
77 if let NestedMeta::Meta(Meta::NameValue(nv)) = nested_meta
78 && nv.path.is_ident("label")
79 && let Lit::Str(lit_str) = nv.lit {
80 return Some(lit_str.value());
81 }
82 }
83 }
84 }
85 None
86}