1 use proc_macro2::TokenStream;
3 use syn::{parse_macro_input, Data, DeriveInput, Fields::Unnamed, Ident, Variant};
5 /// Generates implementation ActivityHandler for an enum, which looks like the following (handling
6 /// all enum variants).
8 /// Based on this code:
10 /// #[derive(serde::Deserialize, serde::Serialize, ActivityHandler)]
11 /// #[serde(untagged)]
12 /// pub enum PersonInboxActivities {
13 /// CreateNote(CreateNote),
14 /// UpdateNote(UpdateNote),
16 /// It will generate this:
18 /// impl ActivityHandler for PersonInboxActivities {
22 /// context: &LemmyContext,
23 /// request_counter: &mut i32,
24 /// ) -> Result<(), LemmyError> {
26 /// PersonInboxActivities::CreateNote(a) => a.verify(context, request_counter).await,
27 /// PersonInboxActivities::UpdateNote(a) => a.verify(context, request_counter).await,
33 /// context: &LemmyContext,
34 /// request_counter: &mut i32,
35 /// ) -> Result<(), LemmyError> {
37 /// PersonInboxActivities::CreateNote(a) => a.receive(context, request_counter).await,
38 /// PersonInboxActivities::UpdateNote(a) => a.receive(context, request_counter).await,
41 /// fn common(&self) -> &ActivityCommonFields {
43 /// PersonInboxActivities::CreateNote(a) => a.common(),
44 /// PersonInboxActivities::UpdateNote(a) => a.common(),
49 #[proc_macro_derive(ActivityHandler)]
50 pub fn derive_activity_handler(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
51 let input = parse_macro_input!(input as DeriveInput);
53 let enum_name = input.ident;
55 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
57 let enum_variants = if let Data::Enum(d) = input.data {
63 let body_verify = quote! {a.verify(context, request_counter).await};
64 let impl_verify = enum_variants
66 .map(|v| generate_match_arm(&enum_name, v, &body_verify));
67 let body_receive = quote! {a.receive(context, request_counter).await};
68 let impl_receive = enum_variants
70 .map(|v| generate_match_arm(&enum_name, v, &body_receive));
72 let expanded = quote! {
73 #[async_trait::async_trait(?Send)]
74 impl #impl_generics lemmy_apub_lib::ActivityHandler for #enum_name #ty_generics #where_clause {
77 context: &lemmy_websocket::LemmyContext,
78 request_counter: &mut i32,
79 ) -> Result<(), lemmy_utils::LemmyError> {
86 context: &lemmy_websocket::LemmyContext,
87 request_counter: &mut i32,
88 ) -> Result<(), lemmy_utils::LemmyError> {
98 fn generate_match_arm(enum_name: &Ident, variant: &Variant, body: &TokenStream) -> TokenStream {
99 let id = &variant.ident;
100 match &variant.fields {
103 #enum_name::#id(a) => #body,
106 _ => unimplemented!(),
110 #[proc_macro_derive(ActivityFields)]
111 pub fn derive_activity_fields(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
112 let input = parse_macro_input!(input as DeriveInput);
114 let name = input.ident;
116 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
118 let expanded = match input.data {
120 let variants = e.variants;
121 let impl_id = variants
123 .map(|v| generate_match_arm(&name, v, "e! {a.id_unchecked()}));
124 let impl_actor = variants
126 .map(|v| generate_match_arm(&name, v, "e! {a.actor()}));
127 let impl_cc = variants
129 .map(|v| generate_match_arm(&name, v, "e! {a.cc()}));
131 impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause {
132 fn id_unchecked(&self) -> &url::Url { match self { #(#impl_id)* } }
133 fn actor(&self) -> &url::Url { match self { #(#impl_actor)* } }
134 fn cc(&self) -> Vec<url::Url> { match self { #(#impl_cc)* } }
139 // check if the struct has a field "cc", and generate impl for cc() function depending on that
140 let has_cc = if let syn::Fields::Named(n) = s.fields {
143 .any(|i| format!("{}", i.ident.as_ref().unwrap()) == "cc")
147 let cc_impl = if has_cc {
148 quote! {self.cc.iter().map(|i| i.clone().into()).collect()}
153 impl #impl_generics lemmy_apub_lib::ActivityFields for #name #ty_generics #where_clause {
154 fn id_unchecked(&self) -> &url::Url { &self.id }
155 fn actor(&self) -> &url::Url { &self.actor.inner() }
156 fn cc(&self) -> Vec<url::Url> { #cc_impl }
160 _ => unimplemented!(),