derive_element.rs

 1use proc_macro::TokenStream;
 2use quote::quote;
 3use syn::{parse_macro_input, DeriveInput, GenericParam};
 4
 5pub fn derive_element(input: TokenStream) -> TokenStream {
 6    let ast = parse_macro_input!(input as DeriveInput);
 7    let type_name = ast.ident;
 8
 9    let mut state_type = quote! { () };
10
11    for param in &ast.generics.params {
12        if let GenericParam::Type(type_param) = param {
13            let type_ident = &type_param.ident;
14            state_type = quote! {#type_ident};
15            break;
16        }
17    }
18
19    let attrs = &ast.attrs;
20    for attr in attrs {
21        if attr.path.is_ident("element") {
22            match attr.parse_meta() {
23                Ok(syn::Meta::List(i)) => {
24                    for nested_meta in i.nested {
25                        if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = nested_meta {
26                            if nv.path.is_ident("view_state") {
27                                if let syn::Lit::Str(lit_str) = nv.lit {
28                                    state_type = lit_str.value().parse().unwrap();
29                                }
30                            }
31                        }
32                    }
33                }
34                _ => (),
35            }
36        }
37    }
38
39    let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
40
41    let gen = quote! {
42        impl #impl_generics gpui2::IntoAnyElement<#state_type> for #type_name #ty_generics
43        #where_clause
44        {
45            fn into_any(self) -> gpui2::AnyElement<#state_type> {
46                gpui2::AnyElement::new(self)
47            }
48        }
49
50        impl #impl_generics gpui2::Element for #type_name #ty_generics
51        #where_clause
52        {
53            type ViewState = #state_type;
54            type ElementState = gpui2::AnyElement<#state_type>;
55
56            fn id(&self) -> Option<gpui2::ElementId> {
57                None
58            }
59
60            fn initialize(
61                &mut self,
62                view_state: &mut Self::ViewState,
63                _: Option<Self::ElementState>,
64                cx: &mut gpui2::ViewContext<Self::ViewState>
65            ) -> Self::ElementState {
66                use gpui2::IntoAnyElement;
67
68                let mut element = self.render(view_state, cx).into_any();
69                element.initialize(view_state, cx);
70                element
71            }
72
73            fn layout(
74                &mut self,
75                view_state: &mut Self::ViewState,
76                rendered_element: &mut Self::ElementState,
77                cx: &mut gpui2::ViewContext<Self::ViewState>,
78            ) -> gpui2::LayoutId {
79                rendered_element.layout(view_state, cx)
80            }
81
82            fn paint(
83                &mut self,
84                bounds: gpui2::Bounds<gpui2::Pixels>,
85                view_state: &mut Self::ViewState,
86                rendered_element: &mut Self::ElementState,
87                cx: &mut gpui2::ViewContext<Self::ViewState>,
88            ) {
89                rendered_element.paint(view_state, cx)
90            }
91        }
92    };
93
94    gen.into()
95}