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            let _ = value;
42            Ok(std::boxed::Box::new(Self {}))
43        }
44    } else {
45        quote! {
46            Ok(std::boxed::Box::new(gpui::serde_json::from_value::<Self>(value)?))
47        }
48    };
49
50    let register_action = register_action(&name);
51
52    let output = quote! {
53        const _: fn() = || {
54            fn assert_impl<T: ?Sized + for<'a> gpui::serde::Deserialize<'a> +  ::std::cmp::PartialEq + ::std::clone::Clone>() {}
55            assert_impl::<#name>();
56        };
57
58        impl gpui::Action for #name {
59            fn name(&self) -> &'static str
60            {
61                ::std::any::type_name::<#name>()
62            }
63
64            fn debug_name() -> &'static str
65            where
66                Self: ::std::marker::Sized
67            {
68                ::std::any::type_name::<#name>()
69            }
70
71            fn build(value: gpui::serde_json::Value) -> gpui::Result<::std::boxed::Box<dyn gpui::Action>>
72            where
73                Self: ::std::marker::Sized {
74                    #build_impl
75            }
76
77            fn partial_eq(&self, action: &dyn gpui::Action) -> bool {
78                action
79                    .as_any()
80                    .downcast_ref::<Self>()
81                    .map_or(false, |a| self == a)
82            }
83
84            fn boxed_clone(&self) ->  std::boxed::Box<dyn gpui::Action> {
85                ::std::boxed::Box::new(self.clone())
86            }
87
88            fn as_any(&self) -> &dyn ::std::any::Any {
89                self
90            }
91        }
92
93        #register_action
94    };
95
96    TokenStream::from(output)
97}