Skip to main content

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    {
59        return has_skip_path(&meta_list.nested);
60    }
61    false
62}
63
64fn has_skip_path(nested: &syn::punctuated::Punctuated<NestedMeta, syn::token::Comma>) -> bool {
65    nested.iter().any(|nested| {
66        if let NestedMeta::Meta(Meta::Path(path)) = nested {
67            return path.is_ident("skip");
68        }
69        false
70    })
71}
72
73pub(crate) fn get_custom_label(attrs: &Vec<syn::Attribute>) -> Option<String> {
74    for attr in attrs {
75        if attr.path.is_ident("enum2egui")
76            && let Ok(syn::Meta::List(meta_list)) = attr.parse_meta()
77        {
78            for nested_meta in meta_list.nested {
79                if let NestedMeta::Meta(Meta::NameValue(nv)) = nested_meta
80                    && nv.path.is_ident("label")
81                    && let Lit::Str(lit_str) = nv.lit
82                {
83                    return Some(lit_str.value());
84                }
85            }
86        }
87    }
88    None
89}