rustpub_macro/
lib.rs

1extern crate proc_macro;
2
3use std::collections::HashMap;
4
5use inflector::Inflector;
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::parse::{Parse, ParseStream, Result};
9use syn::punctuated::Punctuated;
10use syn::{braced, parse_macro_input};
11use syn::{Error, Expr, ExprLit, Ident, Lit, LitStr, Token, Type, Visibility};
12
13mod kw {
14    syn::custom_keyword!(alias);
15    syn::custom_keyword!(brief);
16    syn::custom_keyword!(rename);
17    syn::custom_keyword!(base_traits);
18    syn::custom_keyword!(group_traits);
19    syn::custom_keyword!(extend);
20    syn::custom_keyword!(functional);
21}
22
23struct PropertyStruct {
24    functional: bool,
25    ident: Ident,
26    variants: Punctuated<PropertyVariant, Token![,]>,
27}
28
29impl Parse for PropertyStruct {
30    fn parse(input: ParseStream) -> Result<Self> {
31        let lookahead = input.lookahead1();
32        let functional = if lookahead.peek(kw::functional) {
33            input.parse::<kw::functional>()?;
34            true
35        } else {
36            false
37        };
38        let ident = input.parse()?;
39
40        let content;
41        braced!(content in input);
42        let variants = content.parse_terminated(PropertyVariant::parse)?;
43
44        Ok(PropertyStruct {
45            ident,
46            functional,
47            variants,
48        })
49    }
50}
51
52struct PropertyVariant {
53    ident: Ident,
54    ty: Type,
55}
56
57impl Parse for PropertyVariant {
58    fn parse(input: ParseStream) -> Result<Self> {
59        let ident = input.parse()?;
60        input.parse::<Token![:]>()?;
61        let ty = input.parse()?;
62        Ok(PropertyVariant { ident, ty })
63    }
64}
65
66#[proc_macro]
67pub fn complex_property(input: TokenStream) -> TokenStream {
68    let PropertyStruct {
69        ident,
70        functional,
71        variants,
72    } = parse_macro_input!(input as PropertyStruct);
73
74    let enum_variants = variants.iter().map(|variant| {
75        let ident = &variant.ident;
76        let ty = &variant.ty;
77        let singular_name = format!("{}", ident).to_pascal_case();
78        let plural_name = format!("{}", ident).to_pascal_case().to_plural();
79        let singular_ident = Ident::new(&singular_name, ident.span());
80        let plural_ident = Ident::new(&plural_name, ident.span());
81        match ty {
82            Type::TraitObject(_) => {
83                if functional {
84                    quote! {
85                        #singular_ident(std::boxed::Box<#ty>)
86                    }
87                } else {
88                    quote! {
89                        #singular_ident(std::boxed::Box<#ty>),
90                        #plural_ident(std::vec::Vec<std::boxed::Box<#ty>>)
91                    }
92                }
93            }
94            _ => {
95                if functional {
96                    quote! {
97                        #singular_ident(#ty)
98                    }
99                } else {
100                    quote! {
101                        #singular_ident(#ty),
102                        #plural_ident(std::vec::Vec<#ty>)
103                    }
104                }
105            }
106        }
107    });
108
109    // Not necessary for simple implementations so commenting out for now
110    //    let constructors = variants.iter().map(|variant| {
111    //        let ident = &variant.ident;
112    //        let ty = &variant.ty;
113    //        let singular_name = format!("{}", ident).to_pascal_case();
114    //        let plural_name = format!("{}", ident).to_pascal_case().to_plural();
115    //        let singular_ident = Ident::new(&singular_name, ident.span());
116    //        let plural_ident = Ident::new(&plural_name, ident.span());
117    //
118    //        let singular_constructor_name = format!("{}", ident).to_snake_case();
119    //        let plural_constructor_name = format!("{}", ident).to_snake_case().to_plural();
120    //        let singular_constructor_ident = Ident::new(&singular_constructor_name, ident.span());
121    //        let plural_constructor_ident = Ident::new(&plural_constructor_name, ident.span());
122    //        match ty {
123    //            Type::TraitObject(_) => {
124    //                if functional {
125    //                    quote! {
126    //                        pub fn #singular_constructor_ident(value: std::boxed::Box<#ty>) -> Self {
127    //                            Self::#singular_ident(value)
128    //                        }
129    //                    }
130    //                } else {
131    //                    quote! {
132    //                        pub fn #singular_constructor_ident(value: std::boxed::Box<#ty>) -> Self {
133    //                            Self::#singular_ident(value)
134    //                        }
135    //                        pub fn #plural_constructor_ident(values: std::vec::Vec<std::boxed::Box<#ty>>) -> Self {
136    //                            Self::#plural_ident(values)
137    //                        }
138    //                    }
139    //                }
140    //            }
141    //            _ => {
142    //                if functional {
143    //                    quote! {
144    //                        pub fn #singular_constructor_ident(value: #ty) -> Self {
145    //                            Self::#singular_ident(value)
146    //                        }
147    //                    }
148    //                } else {
149    //                    quote! {
150    //                        pub fn #singular_constructor_ident(value: #ty) -> Self {
151    //                            Self::#singular_ident(value)
152    //                        }
153    //                        pub fn #plural_constructor_ident(values: std::vec::Vec<#ty>) -> Self {
154    //                            Self::#plural_ident(values)
155    //                        }
156    //                    }
157    //                }
158    //            }
159    //        }
160    //    });
161
162    let from_impls = variants.iter().map(|variant| {
163        let enum_ident = &ident;
164        let ident = &variant.ident;
165        let ty = &variant.ty;
166        let singular_name = format!("{}", ident).to_pascal_case();
167        let plural_name = format!("{}", ident).to_pascal_case().to_plural();
168        let singular_ident = Ident::new(&singular_name, ident.span());
169        let plural_ident = Ident::new(&plural_name, ident.span());
170        match ty {
171            Type::TraitObject(_) => {
172                if functional {
173                    quote! {
174                        impl From<std::boxed::Box<#ty>> for #enum_ident {
175                            fn from(value: std::boxed::Box<#ty>) -> Self {
176                                Self::#singular_ident(value)
177                            }
178                        }
179                    }
180                } else {
181                    quote! {
182                        impl From<std::boxed::Box<#ty>> for #enum_ident {
183                            fn from(value: std::boxed::Box<#ty>) -> Self {
184                                Self::#singular_ident(value)
185                            }
186                        }
187                        impl From<std::vec::Vec<std::boxed::Box<#ty>>> for #enum_ident {
188                            fn from(value: std::vec::Vec<std::boxed::Box<#ty>>) -> Self {
189                                Self::#plural_ident(value)
190                            }
191                        }
192                    }
193                }
194            }
195            _ => {
196                if functional {
197                    quote! {
198                        impl std::convert::From<#ty> for #enum_ident {
199                            fn from(value: #ty) -> Self {
200                                Self::#singular_ident(value)
201                            }
202                        }
203                    }
204                } else {
205                    quote! {
206                        impl std::convert::From<#ty> for #enum_ident {
207                            fn from(value: #ty) -> Self {
208                                Self::#singular_ident(value)
209                            }
210                        }
211                        impl std::convert::From<std::vec::Vec<#ty>> for #enum_ident {
212                            fn from(values: std::vec::Vec<#ty>) -> Self {
213                                Self::#plural_ident(values)
214                            }
215                        }
216                    }
217                }
218            }
219        }
220    });
221
222    let expanded = quote! {
223            #[derive(std::clone::Clone, std::fmt::Debug, serde::Serialize, serde::Deserialize)]
224            #[serde(untagged)]
225            pub enum #ident {
226                None,
227                #(#enum_variants,)*
228            }
229
230    //        impl #ident {
231    //            #(#constructors)*
232    //        }
233
234            impl Default for #ident {
235                fn default() -> Self {
236                    Self::None
237                }
238            }
239
240            impl ActivitypubProperty for #ident {
241                fn is_none(&self) -> bool {
242                    match self {
243                        #ident::None => true,
244                        _ => false,
245                    }
246                }
247
248                fn is_some(&self) -> bool {
249                    match self {
250                        #ident::None => false,
251                        _ => true,
252                    }
253                }
254            }
255
256            #(#from_impls)*
257        };
258    expanded.into()
259}
260
261// TODO: change this to actually get types, but still be able to make the variant names
262struct ActivitypubProperty {
263    visibility: Visibility,
264    name: Ident,
265    functional: bool,
266    possible_ty: Punctuated<Ident, Token![,]>,
267}
268
269impl Parse for ActivitypubProperty {
270    fn parse(input: ParseStream) -> Result<Self> {
271        let visibility = input.parse()?;
272        let name = input.parse()?;
273        input.parse::<Token![,]>()?;
274        let functional_expr: Expr = input.parse()?;
275        input.parse::<Token![,]>()?;
276        let possible_ty = input.parse_terminated(Ident::parse)?;
277
278        let functional = if let Expr::Lit(ExprLit {
279            lit: Lit::Bool(b), ..
280        }) = functional_expr
281        {
282            b.value
283        } else {
284            return Err(Error::new_spanned(
285                functional_expr,
286                "expected true or false",
287            ));
288        };
289
290        Ok(ActivitypubProperty {
291            visibility,
292            name,
293            possible_ty,
294            functional,
295        })
296    }
297}
298
299// Default to using a vec for non-functional fields
300#[proc_macro]
301pub fn activitypub_property(input: TokenStream) -> TokenStream {
302    activitypub_property_vec(input)
303}
304
305#[proc_macro]
306pub fn activitypub_property_vec(input: TokenStream) -> TokenStream {
307    let ActivitypubProperty {
308        visibility,
309        name,
310        possible_ty,
311        functional,
312    } = parse_macro_input!(input as ActivitypubProperty);
313
314    let variants = possible_ty.iter().map(|ty| {
315        let singular_name = format!("{}", ty).to_pascal_case();
316        let plural_name = format!("{}", ty).to_pascal_case().to_plural();
317        let singular_ident = Ident::new(&singular_name, ty.span());
318        let plural_ident = Ident::new(&plural_name, ty.span());
319        if functional {
320            quote! {
321                #singular_ident(Box<#ty>)
322            }
323        } else {
324            quote! {
325                #singular_ident(Box<#ty>),
326                #plural_ident(Vec<#ty>)
327            }
328        }
329    });
330
331    let constructors = possible_ty.iter().map(|ty| {
332        let singular_name = format!("{}", ty).to_pascal_case();
333        let plural_name = format!("{}", ty).to_pascal_case().to_plural();
334        let singular_ident = Ident::new(&singular_name, ty.span());
335        let plural_ident = Ident::new(&plural_name, ty.span());
336
337        let singular_constructor_name = format!("{}", ty).to_snake_case();
338        let plural_constructor_name = format!("{}", ty).to_snake_case().to_plural();
339        let singular_constructor_ident = Ident::new(&singular_constructor_name, ty.span());
340        let plural_constructor_ident = Ident::new(&plural_constructor_name, ty.span());
341        if functional {
342            quote! {
343                #visibility fn #singular_constructor_ident(value: #ty) -> Self {
344                    Self::#singular_ident(Box::new(value))
345                }
346            }
347        } else {
348            quote! {
349                #visibility  fn #singular_constructor_ident(value: #ty) -> Self {
350                    Self::#singular_ident(Box::new(value))
351                }
352                #visibility  fn #plural_constructor_ident(values: Vec<#ty>) -> Self {
353                    Self::#plural_ident(values)
354                }
355            }
356        }
357    });
358
359    let expanded = quote! {
360        #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
361        #[serde(untagged)]
362        #visibility enum #name {
363            None,
364            #(#variants,)*
365        }
366
367        impl #name {
368            #(#constructors)*
369        }
370
371        impl Default for #name {
372            fn default() -> Self {
373                Self::None
374            }
375        }
376
377        impl ActivitypubProperty for #name {
378            fn is_none(&self) -> bool {
379                self == &Self::None
380            }
381
382            fn is_some(&self) -> bool {
383                self != &Self::None
384            }
385        }
386    };
387
388    expanded.into()
389}
390
391#[proc_macro]
392pub fn activitypub_property_hashmap(input: TokenStream) -> TokenStream {
393    let ActivitypubProperty {
394        visibility,
395        name,
396        possible_ty,
397        functional,
398    } = parse_macro_input!(input as ActivitypubProperty);
399
400    let variants = possible_ty.iter().map(|ty| {
401        let singular_name = format!("{}", ty).to_pascal_case();
402        let plural_name = format!("{}", ty).to_pascal_case().to_plural();
403        let singular_ident = Ident::new(&singular_name, ty.span());
404        let plural_ident = Ident::new(&plural_name, ty.span());
405        if functional {
406            quote! {
407                #singular_ident(Box<#ty>)
408            }
409        } else {
410            quote! {
411                #singular_ident(Box<#ty>),
412                #plural_ident(HashMap<String, #ty>)
413            }
414        }
415    });
416
417    let constructors = possible_ty.iter().map(|ty| {
418        let singular_name = format!("{}", ty).to_pascal_case();
419        let plural_name = format!("{}", ty).to_pascal_case().to_plural();
420        let singular_ident = Ident::new(&singular_name, ty.span());
421        let plural_ident = Ident::new(&plural_name, ty.span());
422
423        let singular_constructor_name = format!("{}", ty).to_snake_case();
424        let plural_constructor_name = format!("{}", ty).to_snake_case().to_plural();
425        let singular_constructor_ident = Ident::new(&singular_constructor_name, ty.span());
426        let plural_constructor_ident = Ident::new(&plural_constructor_name, ty.span());
427        if functional {
428            quote! {
429                #visibility fn #singular_constructor_ident(value: #ty) -> Self {
430                    Self::#singular_ident(Box::new(value))
431                }
432            }
433        } else {
434            quote! {
435                #visibility  fn #singular_constructor_ident(value: #ty) -> Self {
436                    Self::#singular_ident(Box::new(value))
437                }
438                #visibility  fn #plural_constructor_ident(values: HashMap<String, #ty>) -> Self {
439                    Self::#plural_ident(values)
440                }
441            }
442        }
443    });
444
445    let expanded = quote! {
446        #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
447        #[serde(untagged)]
448        #visibility enum #name {
449            None,
450            #(#variants,)*
451        }
452
453        impl #name {
454            #(#constructors)*
455        }
456
457        impl Default for #name {
458            fn default() -> Self {
459                Self::None
460            }
461        }
462
463        impl ActivitypubProperty for #name {
464            fn is_none(&self) -> bool {
465                self == &Self::None
466            }
467
468            fn is_some(&self) -> bool {
469                self != &Self::None
470            }
471        }
472    };
473
474    expanded.into()
475}
476
477struct ActivitypubStruct {
478    visibility: Visibility,
479    ident: Ident,
480    fields: Punctuated<ActivitypubField, Token![,]>,
481    brief: ActivitypubField,
482}
483
484#[derive(Clone)]
485struct ActivitypubField {
486    visibility: Visibility,
487    ident: Ident,
488    alias: Option<LitStr>,
489    rename: Option<LitStr>,
490    ty: Type,
491}
492
493impl Parse for ActivitypubStruct {
494    fn parse(input: ParseStream) -> Result<Self> {
495        let content;
496        let visibility = input.parse()?;
497        input.parse::<Token![struct]>()?;
498        let ident = input.parse()?;
499        braced!(content in input);
500        let fields = content.parse_terminated(ActivitypubField::parse)?;
501        input.parse::<kw::brief>()?;
502        let brief_ident: Ident = input.parse()?;
503        let brief = fields
504            .iter()
505            .find(|field| field.ident.to_string() == brief_ident.to_string())
506            .unwrap()
507            .clone();
508
509        Ok(ActivitypubStruct {
510            visibility,
511            ident,
512            fields,
513            brief,
514        })
515    }
516}
517
518impl Parse for ActivitypubField {
519    fn parse(input: ParseStream) -> Result<Self> {
520        let visibility = input.parse()?;
521        let ident = input.parse()?;
522        // NOTE: this only looks at the next token at the time of
523        // creation, it does not advance as input is parsed.
524        // So, it needs to be created right before it is used.
525        let lookahead = input.lookahead1();
526        let (alias, rename) = if lookahead.peek(kw::alias) {
527            input.parse::<kw::alias>()?;
528            (Some(input.parse()?), None)
529        } else if lookahead.peek(kw::rename) {
530            input.parse::<kw::rename>()?;
531            (None, Some(input.parse()?))
532        } else {
533            (None, None)
534        };
535
536        input.parse::<Token![:]>()?;
537
538        Ok(ActivitypubField {
539            visibility,
540            ident,
541            alias,
542            rename,
543            ty: input.parse()?,
544        })
545    }
546}
547
548#[proc_macro]
549pub fn activitypub_core(input: TokenStream) -> TokenStream {
550    let ActivitypubStruct {
551        visibility,
552        ident,
553        fields,
554        brief,
555    } = parse_macro_input!(input as ActivitypubStruct);
556
557    let struct_fields = fields.iter().map(|field| {
558        let visibility = &field.visibility;
559        let ident = &field.ident;
560        let ty = &field.ty;
561        if let Some(alias) = &field.alias {
562            quote! {
563                #[serde(alias = #alias)]
564                #visibility #ident: #ty
565            }
566        } else if let Some(rename) = &field.rename {
567            quote! {
568                #[serde(rename = #rename)]
569                #visibility #ident: #ty
570            }
571        } else {
572            quote! {
573                #visibility #ident: #ty
574            }
575        }
576    });
577
578    let enum_fields = fields.iter().map(|field| {
579        let ident = &field.ident;
580        let ty = &field.ty;
581        if let Some(alias) = &field.alias {
582            quote! {
583                #[serde(alias = #alias, skip_serializing_if = "ActivitypubProperty::is_none", default)]
584                #ident: #ty
585            }
586        } else if let Some(rename) = &field.rename {
587            quote! {
588                #[serde(rename = #rename, skip_serializing_if = "ActivitypubProperty::is_none", default)]
589                #ident: #ty
590            }
591        } else {
592            quote! {
593                #[serde(skip_serializing_if = "ActivitypubProperty::is_none", default)]
594                #ident: #ty
595            }
596        }
597    });
598
599    let enum_ident_string = format!("{}Enum", ident);
600    let enum_ident = Ident::new(&enum_ident_string, ident.span());
601    let builder_ident = Ident::new(&format!("{}Builder", ident), ident.span());
602    let brief_ident = &brief.ident;
603    let brief_ty = &brief.ty;
604
605    let from_enum_fields = fields.iter().map(|field| {
606        let ident = &field.ident;
607        quote! { #ident }
608    });
609
610    let from_enum_fields2 = fields.iter().map(|field| {
611        let ident = &field.ident;
612        quote! { #ident }
613    });
614
615    let from_struct_fields = fields.iter().map(|field| {
616        let ident = &field.ident;
617        if ident.to_string() != brief_ident.to_string() {
618            quote! { && struct_value.#ident.is_none() }
619        } else {
620            quote! {}
621        }
622    });
623
624    let from_struct_fields2 = fields.iter().map(|field| {
625        let ident = &field.ident;
626        quote! { #ident: struct_value.#ident }
627    });
628
629    let expanded = quote! {
630        #[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize, derive_builder::Builder)]
631        #[serde(rename_all = "camelCase", from = #enum_ident_string, into = #enum_ident_string)]
632        #[builder(default, setter(strip_option))]
633        #visibility struct #ident {
634            #(#struct_fields,)*
635        }
636
637        #[derive(Clone, Debug, PartialEq, serde::Serialize, serde::Deserialize)]
638        #[serde(untagged)]
639        #visibility enum #enum_ident {
640            Brief(String),
641            #[serde(rename_all = "camelCase")]
642            Full {
643                #(#enum_fields,)*
644            },
645        }
646
647        impl From<#enum_ident> for #ident {
648            fn from(enum_value: #enum_ident) -> Self {
649                match enum_value {
650                    #enum_ident::Brief(#brief_ident) => #builder_ident::default()
651                        .#brief_ident(#brief_ty::String(Box::new(#brief_ident)))
652                        .build()
653                        .unwrap(),
654                    #enum_ident::Full {
655                        #(#from_enum_fields,)*
656                    } => {
657                        #ident {
658                            #(#from_enum_fields2,)*
659                        }
660                    },
661                }
662            }
663        }
664
665        impl From<#ident> for #enum_ident {
666            fn from(struct_value: #ident) -> Self {
667                if struct_value.#brief_ident.is_some()
668                #(#from_struct_fields)* {
669                    match struct_value.#brief_ident {
670                        #brief_ty::String(#brief_ident) => #enum_ident::Brief(#brief_ident.to_string()),
671                        _ => unreachable!(),
672                    }
673                } else {
674                    #enum_ident::Full {
675                        #(#from_struct_fields2,)*
676                    }
677                }
678            }
679        }
680    };
681
682    expanded.into()
683}
684
685struct ComplexStruct {
686    base_traits: Punctuated<Ident, Token![,]>,
687    group_traits: Punctuated<GroupTrait, Token![,]>,
688    type_groups: Punctuated<TypeGroup, Token![,]>,
689}
690
691impl Parse for ComplexStruct {
692    fn parse(input: ParseStream) -> Result<Self> {
693        input.parse::<kw::base_traits>()?;
694
695        let content;
696        braced!(content in input);
697        let base_traits = content.parse_terminated(Ident::parse)?;
698
699        input.parse::<kw::group_traits>()?;
700        let content;
701        braced!(content in input);
702        let group_traits = content.parse_terminated(GroupTrait::parse)?;
703
704        let type_groups = input.parse_terminated(TypeGroup::parse)?;
705
706        Ok(ComplexStruct {
707            base_traits,
708            group_traits,
709            type_groups,
710        })
711    }
712}
713
714struct GroupTrait {
715    ident: Ident,
716    parent: Ident,
717}
718
719impl Parse for GroupTrait {
720    fn parse(input: ParseStream) -> Result<Self> {
721        let ident = input.parse()?;
722        input.parse::<Token![:]>()?;
723        let parent = input.parse()?;
724
725        Ok(GroupTrait { ident, parent })
726    }
727}
728
729struct TypeGroup {
730    ident: Ident,
731    types: Punctuated<TypeDefinition, Token![,]>,
732}
733
734impl Parse for TypeGroup {
735    fn parse(input: ParseStream) -> Result<Self> {
736        let ident = input.parse()?;
737
738        let content;
739        braced!(content in input);
740        let types = content.parse_terminated(TypeDefinition::parse)?;
741
742        Ok(TypeGroup { ident, types })
743    }
744}
745
746#[derive(Clone)]
747struct TypeDefinition {
748    ident: Ident,
749    parent: Option<Ident>,
750    fields: Punctuated<TypeField, Token![,]>,
751}
752
753impl Parse for TypeDefinition {
754    fn parse(input: ParseStream) -> Result<Self> {
755        let ident = input.parse()?;
756
757        let lookahead = input.lookahead1();
758        let parent = if lookahead.peek(kw::extend) {
759            input.parse::<kw::extend>()?;
760            Some(input.parse()?)
761        } else {
762            None
763        };
764
765        let content;
766        braced!(content in input);
767        let fields = content.parse_terminated(TypeField::parse)?;
768
769        Ok(TypeDefinition {
770            ident,
771            parent,
772            fields,
773        })
774    }
775}
776
777#[derive(Clone)]
778struct TypeField {
779    ident: Ident,
780    alias: Option<LitStr>,
781    rename: Option<LitStr>,
782    ty: Type,
783}
784
785impl Parse for TypeField {
786    fn parse(input: ParseStream) -> Result<Self> {
787        let ident = input.parse()?;
788
789        let lookahead = input.lookahead1();
790        let (alias, rename) = if lookahead.peek(kw::alias) {
791            input.parse::<kw::alias>()?;
792            (Some(input.parse()?), None)
793        } else if lookahead.peek(kw::rename) {
794            input.parse::<kw::rename>()?;
795            (None, Some(input.parse()?))
796        } else {
797            (None, None)
798        };
799
800        input.parse::<Token![:]>()?;
801        let ty = input.parse()?;
802
803        Ok(TypeField {
804            ident,
805            alias,
806            rename,
807            ty,
808        })
809    }
810}
811
812#[derive(Clone)]
813struct FullTypeDefinition {
814    ident: Ident,
815    fields: Vec<TypeField>,
816}
817
818#[proc_macro]
819pub fn activitypub_complex(input: TokenStream) -> TokenStream {
820    let ComplexStruct {
821        base_traits,
822        group_traits,
823        type_groups,
824    } = parse_macro_input!(input as ComplexStruct);
825
826    /*
827    TODO:
828    1. make base traits
829    2. make group traits
830    3. get all of the types and fill out their fields from the extends
831    4. get all of the types for each group trait and add as_TYPE methods on impl dyn TRAIT
832    5. make the types
833    6. implement the base and group traits for the types
834    7. make the from impls
835    */
836    // TODO: see if possible to add serde(other) to typetag to set default impl
837
838    // 1. make base traits
839    let base_trait_defs = base_traits.iter().map(|base_trait| {
840        quote! {
841            #[typetag::serde(tag = "type")]
842            pub trait #base_trait: std::fmt::Debug {
843                fn any_ref(&self) -> &dyn std::any::Any;
844                fn kind(&self) -> &str;
845                fn box_clone(&self) -> Box<dyn #base_trait>;
846            }
847
848            impl std::clone::Clone for Box<dyn #base_trait> {
849                fn clone(&self) -> Box<dyn #base_trait> {
850                    self.box_clone()
851                }
852            }
853        }
854    });
855
856    // 2. make group traits
857    let group_trait_defs = group_traits.iter().map(|group_trait| {
858        let ident = &group_trait.ident;
859        //        let parent = &group_trait.parent;
860        quote! {
861            #[typetag::serde(tag = "type")]
862            pub trait #ident: std::fmt::Debug {
863                fn any_ref(&self) -> &dyn std::any::Any;
864                fn box_clone(&self) -> Box<dyn #ident>;
865            }
866
867            impl std::clone::Clone for Box<dyn #ident> {
868                fn clone(&self) -> Box<dyn #ident> {
869                    self.box_clone()
870                }
871            }
872        }
873    });
874
875    // 3. get all of the types and fill out their fields from the extends
876    let mut type_definitions: HashMap<Ident, TypeDefinition> = HashMap::new();
877    let mut full_type_definitions: HashMap<Ident, FullTypeDefinition> = HashMap::new();
878
879    for type_group in type_groups.iter() {
880        for type_definition in type_group.types.iter() {
881            type_definitions.insert(type_definition.ident.clone(), type_definition.clone());
882        }
883    }
884
885    while !type_definitions.is_empty() {
886        let temp_type_definitions = type_definitions.clone();
887        let keys = temp_type_definitions.keys();
888        for key in keys {
889            let type_definition = type_definitions.get(key).unwrap();
890            let mut full_type_definition = FullTypeDefinition {
891                ident: type_definition.ident.clone(),
892                fields: type_definition
893                    .fields
894                    .iter()
895                    .map(|type_field| type_field.clone())
896                    .collect(),
897            };
898            // this type extends another, need to try to get parent fields
899            if let Some(parent) = &type_definition.parent {
900                // parent has already been expanded, so we can get the fields
901                if let Some(parent_definition) = full_type_definitions.get(parent) {
902                    let parent_fields = parent_definition.fields.clone();
903                    full_type_definition.fields.extend(parent_fields);
904                    full_type_definitions
905                        .insert(full_type_definition.ident.clone(), full_type_definition);
906                    type_definitions.remove(key);
907                } // parent has not been expanded yet, need to try again next round
908            } else {
909                // there's no parent, it's already fully expanded and we can add it
910                full_type_definitions
911                    .insert(full_type_definition.ident.clone(), full_type_definition);
912                type_definitions.remove(key);
913            }
914        }
915    }
916
917    // 4. get all of the types for each group trait and add as_TYPE methods on impl dyn TRAIT
918    let group_trait_dyn_impls = type_groups.iter().map(|type_group| {
919        let ident = &type_group.ident;
920        let as_methods = type_group.types.iter().map(|type_definition| {
921            let ident = &type_definition.ident;
922            let method_name = format!("as_{}", ident).to_snake_case();
923            let as_method_ident = Ident::new(&method_name, ident.span());
924            quote! {
925                pub fn #as_method_ident(&self) -> std::option::Option<&#ident> {
926                    self.downcast_ref::<#ident>()
927                }
928            }
929        });
930        quote! {
931            impl dyn #ident {
932                pub fn downcast_ref<T: std::any::Any>(&self) -> std::option::Option<&T> {
933                    self.any_ref().downcast_ref()
934                }
935                #(#as_methods)*
936            }
937        }
938    });
939
940    // 5. make the types
941    let type_defs = full_type_definitions
942        .iter()
943        .map(|(ident, full_type_definition)| {
944            let struct_fields = full_type_definition.fields.iter().map(|type_field| {
945                let ident = &type_field.ident;
946                let ty = &type_field.ty;
947                if let Some(alias) = &type_field.alias {
948                    quote! {
949                        #[serde(alias = #alias)]
950                        pub #ident: #ty
951                    }
952                } else if let Some(rename) = &type_field.rename {
953                    quote! {
954                        #[serde(rename = #rename)]
955                        pub #ident: #ty
956                    }
957                } else {
958                    quote! {
959                        pub #ident: #ty
960                    }
961                }
962            });
963            let enum_fields = full_type_definition.fields.iter().map(|type_field| {
964                let ident = &type_field.ident;
965                let ty = &type_field.ty;
966                if let Some(alias) = &type_field.alias {
967                    quote! {
968                        #[serde(alias = #alias, skip_serializing_if = "ActivitypubProperty::is_none", default)]
969                        #ident: #ty
970                    }
971                } else if let Some(rename) = &type_field.rename {
972                    quote! {
973                        #[serde(rename = #rename, skip_serializing_if = "ActivitypubProperty::is_none", default)]
974                        #ident: #ty
975                    }
976                } else {
977                    quote! {
978                        #[serde(skip_serializing_if = "ActivitypubProperty::is_none", default)]
979                        #ident: #ty
980                    }
981                }
982            });
983            let enum_ident_string = format!("{}Enum", ident);
984            let enum_ident = Ident::new(&enum_ident_string, ident.span());
985            quote! {
986                #[derive(std::clone::Clone, std::fmt::Debug, std::default::Default, serde::Deserialize, serde::Serialize, derive_builder::Builder)]
987                #[serde(rename_all = "camelCase", from = #enum_ident_string, into = #enum_ident_string)]
988                #[builder(default, setter(into))]
989                pub struct #ident {
990                    #(#struct_fields,)*
991                }
992
993                #[derive(std::clone::Clone, std::fmt::Debug, serde::Deserialize, serde::Serialize)]
994                #[serde(untagged)]
995                pub enum #enum_ident {
996                    Brief(String),
997                    #[serde(rename_all = "camelCase")]
998                    Full {
999                        #(#enum_fields,)*
1000                    },
1001                }
1002            }
1003        });
1004
1005    // 6. implement the base and group traits for the types
1006    // make hashmap<ident, vec<ident>> of base_traits to group_traits that require them
1007    // make hashmap<ident, ident> of group_traits to base_traits that they require
1008    let mut base_to_group_traits: HashMap<Ident, Vec<Ident>> = HashMap::new();
1009    let mut group_to_base_traits: HashMap<Ident, Ident> = HashMap::new();
1010    for group_trait in group_traits.iter() {
1011        group_to_base_traits.insert(group_trait.ident.clone(), group_trait.parent.clone());
1012        if base_to_group_traits.contains_key(&group_trait.parent) {
1013            base_to_group_traits
1014                .get_mut(&group_trait.parent)
1015                .unwrap()
1016                .push(group_trait.ident.clone());
1017        } else {
1018            base_to_group_traits
1019                .insert(group_trait.parent.clone(), vec![group_trait.ident.clone()]);
1020        }
1021    }
1022
1023    let trait_impls = type_groups.iter().map(|type_group| {
1024        let group_ident = &type_group.ident;
1025        let base_ident = group_to_base_traits.get(group_ident).unwrap();
1026        let type_trait_impls = type_group.types.iter().map(|type_definition| {
1027            let ident = &type_definition.ident;
1028            let kind = format!("{}", ident);
1029            quote! {
1030                #[typetag::serde]
1031                impl #base_ident for #ident {
1032                    fn any_ref(&self) -> &dyn std::any::Any {
1033                        self
1034                    }
1035                    fn kind(&self) -> &str {
1036                        #kind
1037                    }
1038                    fn box_clone(&self) -> Box<dyn #base_ident> {
1039                        Box::new((*self).clone())
1040                    }
1041                }
1042                #[typetag::serde]
1043                impl #group_ident for #ident {
1044                    fn any_ref(&self) -> &dyn std::any::Any {
1045                        self
1046                    }
1047                    fn box_clone(&self) -> Box<dyn #group_ident> {
1048                        Box::new((*self).clone())
1049                    }
1050                }
1051            }
1052        });
1053        quote! {
1054            #(#type_trait_impls)*
1055        }
1056    });
1057
1058    // 7. make the from impls
1059    let from_impls = full_type_definitions
1060        .iter()
1061        .map(|(ident, full_type_definition)| {
1062            let enum_ident_string = format!("{}Enum", ident);
1063            let enum_ident = Ident::new(&enum_ident_string, ident.span());
1064            let builder_ident = Ident::new(&format!("{}Builder", ident), ident.span());
1065            let brief_ident = match &full_type_definition.ident.to_string()[..] {
1066                "Link" | "Mention" => Ident::new("href", full_type_definition.ident.span()),
1067                _ => Ident::new("id", full_type_definition.ident.span()),
1068            };
1069
1070            let brief = full_type_definition
1071                .fields
1072                .iter()
1073                .find(|type_field| type_field.ident.to_string() == brief_ident.to_string())
1074                .unwrap()
1075                .clone();
1076            let brief_ident = &brief.ident;
1077            let brief_ty = &brief.ty;
1078
1079            let from_enum_fields = full_type_definition.fields.iter().map(|type_field| {
1080                let ident = &type_field.ident;
1081                quote! { #ident }
1082            });
1083
1084            let from_enum_fields2 = full_type_definition.fields.iter().map(|type_field| {
1085                let ident = &type_field.ident;
1086                quote! { #ident }
1087            });
1088
1089            let from_struct_fields = full_type_definition.fields.iter().map(|type_field| {
1090                let ident = &type_field.ident;
1091                if ident.to_string() != brief_ident.to_string() {
1092                    quote! { && struct_value.#ident.is_none() }
1093                } else {
1094                    quote! {}
1095                }
1096            });
1097
1098            let from_struct_fields2 = full_type_definition.fields.iter().map(|type_field| {
1099                let ident = &type_field.ident;
1100                quote! { #ident: struct_value.#ident }
1101            });
1102
1103            quote! {
1104                impl From<#enum_ident> for #ident {
1105                    fn from(enum_value: #enum_ident) -> Self {
1106                        match enum_value {
1107                            #enum_ident::Brief(#brief_ident) => #builder_ident::default()
1108                                .#brief_ident(#brief_ty::String(#brief_ident))
1109                                .build()
1110                                .unwrap(),
1111                            #enum_ident::Full {
1112                                #(#from_enum_fields,)*
1113                            } => {
1114                                #ident {
1115                                    #(#from_enum_fields2,)*
1116                                }
1117                            },
1118                        }
1119                    }
1120                }
1121
1122                impl From<#ident> for #enum_ident {
1123                    fn from(struct_value: #ident) -> Self {
1124                        if struct_value.#brief_ident.is_some()
1125                        #(#from_struct_fields)* {
1126                            match struct_value.#brief_ident {
1127                                #brief_ty::String(#brief_ident) => #enum_ident::Brief(#brief_ident),
1128                                _ => unreachable!(),
1129                            }
1130                        } else {
1131                            #enum_ident::Full {
1132                                #(#from_struct_fields2,)*
1133                            }
1134                        }
1135                    }
1136                }
1137            }
1138        });
1139
1140    let expanded = quote! {
1141        #(#base_trait_defs)*
1142        #(#group_trait_defs)*
1143        #(#group_trait_dyn_impls)*
1144        #(#type_defs)*
1145        #(#trait_impls)*
1146        #(#from_impls)*
1147    };
1148    expanded.into()
1149}