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}