action.rs

 1// Input:
 2//
 3// #[action]
 4// struct Foo {
 5//   bar: String,
 6// }
 7
 8// Output:
 9//
10// #[gpui::register_action]
11// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::default::Default, std::fmt::Debug)]
12// struct Foo {
13//   bar: String,
14// }
15
16use proc_macro::TokenStream;
17use quote::quote;
18use syn::{parse_macro_input, DeriveInput, Error};
19
20use crate::register_action::register_action;
21
22pub fn action(input: TokenStream) -> TokenStream {
23    let input = parse_macro_input!(input as DeriveInput);
24
25    let name = &input.ident;
26
27    if input.generics.lt_token.is_some() {
28        return Error::new(name.span(), "Actions must be a concrete type")
29            .into_compile_error()
30            .into();
31    }
32
33    let is_unit_struct = match input.data {
34        syn::Data::Struct(struct_data) => struct_data.fields.is_empty(),
35        syn::Data::Enum(_) => false,
36        syn::Data::Union(_) => false,
37    };
38
39    let build_impl = if is_unit_struct {
40        quote! {
41            Ok(std::boxed::Box::new(Self {}))
42        }
43    } else {
44        quote! {
45            Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
46        }
47    };
48
49    let register_action = register_action(&name);
50
51    let output = quote! {
52        const _: fn() = || {
53            fn assert_impl<T: ?Sized + for<'a> gpui::serde::Deserialize<'a> +  ::std::cmp::PartialEq + ::std::clone::Clone>() {}
54            assert_impl::<#name>();
55        };
56
57        impl gpui::Action for #name {
58            fn name(&self) -> &'static str
59            {
60                ::std::any::type_name::<#name>()
61            }
62
63            fn debug_name() -> &'static str
64            where
65                Self: ::std::marker::Sized
66            {
67                ::std::any::type_name::<#name>()
68            }
69
70            fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>>
71            where
72                Self: ::std::marker::Sized {
73                    #build_impl
74            }
75
76            fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
77                action
78                    .as_any()
79                    .downcast_ref::<Self>()
80                    .map_or(false, |a| self == a)
81            }
82
83            fn boxed_clone(&self) ->  std::boxed::Box<dyn gpui::Action> {
84                ::std::boxed::Box::new(self.clone())
85            }
86
87            fn as_any(&self) -> &dyn ::std::any::Any {
88                self
89            }
90        }
91
92        #register_action
93    };
94
95    TokenStream::from(output)
96}