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}