enum2egui_derive/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
mod enums;
mod structs;

use enums::derive_enum;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use quote::{quote, ToTokens};
use structs::derive_struct;
use syn::{parse_macro_input, Attribute, Data, DeriveInput, Error, Lit, Meta, NestedMeta};

macro_rules! derive_error {
    ($string: tt) => {
        Error::new(Span::call_site(), $string)
            .to_compile_error()
            .into()
    };
}

#[proc_macro_derive(Gui, attributes(enum2egui))]
pub fn derive_gui(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let name = &input.ident;

    match &input.data {
        Data::Struct(data) => derive_struct(name, data),
        Data::Enum(data) => derive_enum(name, data),
        Data::Union(..) => derive_error!("enum2egui does not support unions"),
    }
}

pub(crate) fn derive_trait(
    name: &Ident,
    gui: proc_macro2::TokenStream,
    gui_mut: proc_macro2::TokenStream,
) -> TokenStream {
    quote! {
        impl enum2egui::GuiInspect for #name {
            fn ui(&self, ui: &mut egui::Ui) {
                #gui
            }

            fn ui_mut(&mut self, ui: &mut egui::Ui) {
                #gui_mut
            }
        }
    }
    .to_token_stream()
    .into()
}

pub(crate) fn has_skip_attr(attrs: &[Attribute]) -> bool {
    attrs.iter().any(is_enum2egui_skip_attr)
}

fn is_enum2egui_skip_attr(attr: &Attribute) -> bool {
    if let Ok(Meta::List(meta_list)) = attr.parse_meta() {
        if meta_list.path.is_ident("enum2egui") {
            return has_skip_path(&meta_list.nested);
        }
    }
    false
}

fn has_skip_path(nested: &syn::punctuated::Punctuated<NestedMeta, syn::token::Comma>) -> bool {
    nested.iter().any(|nested| {
        if let NestedMeta::Meta(Meta::Path(path)) = nested {
            return path.is_ident("skip");
        }
        false
    })
}

pub(crate) fn get_custom_label(attrs: &Vec<syn::Attribute>) -> Option<String> {
    for attr in attrs {
        if attr.path.is_ident("enum2egui") {
            if let Ok(syn::Meta::List(meta_list)) = attr.parse_meta() {
                for nested_meta in meta_list.nested {
                    if let NestedMeta::Meta(Meta::NameValue(nv)) = nested_meta {
                        if nv.path.is_ident("label") {
                            if let Lit::Str(lit_str) = nv.lit {
                                return Some(lit_str.value());
                            }
                        }
                    }
                }
            }
        }
    }
    None
}