enum2egui_derive/
lib.rs

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}