1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
extern crate proc_macro;
use inflector::Inflector;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Error, Expr, ExprLit, Ident, Lit, Token, Visibility};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
struct ActivitypubProperty {
visibility: Visibility,
name: Ident,
functional: bool,
possible_ty: Punctuated<Ident, Token![,]>,
}
impl Parse for ActivitypubProperty {
fn parse(input: ParseStream) -> Result<Self> {
let visibility = input.parse()?;
let name = input.parse()?;
input.parse::<Token![,]>()?;
let functional_expr: Expr = input.parse()?;
input.parse::<Token![,]>()?;
let possible_ty = input.parse_terminated(Ident::parse)?;
let functional = if let Expr::Lit(ExprLit { lit: Lit::Bool(b), .. }) = functional_expr {
b.value
} else {
return Err(Error::new_spanned(functional_expr, "expected true or false"));
};
Ok(ActivitypubProperty {
visibility,
name,
possible_ty,
functional,
})
}
}
#[proc_macro]
pub fn activitypub_property(input: TokenStream) -> TokenStream {
activitypub_property_vec(input)
}
#[proc_macro]
pub fn activitypub_property_vec(input: TokenStream) -> TokenStream {
let ActivitypubProperty {
visibility,
name,
possible_ty,
functional
} = parse_macro_input!(input as ActivitypubProperty);
let variants = possible_ty.iter().map(|ty| {
let singular_name = format!("{}", ty).to_pascal_case();
let plural_name = format!("{}", ty).to_pascal_case().to_plural();
let singular_ident = Ident::new(&singular_name, ty.span());
let plural_ident = Ident::new(&plural_name, ty.span());
if functional {
quote! {
#singular_ident(Box<#ty>)
}
} else {
quote! {
#singular_ident(Box<#ty>),
#plural_ident(Vec<#ty>)
}
}
});
let expanded = quote! {
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
#visibility enum #name {
#(#variants,)*
}
};
expanded.into()
}
#[proc_macro]
pub fn activitypub_property_hashmap(input: TokenStream) -> TokenStream {
let ActivitypubProperty {
visibility,
name,
possible_ty,
functional
} = parse_macro_input!(input as ActivitypubProperty);
let variants = possible_ty.iter().map(|ty| {
let singular_name = format!("{}", ty).to_pascal_case();
let plural_name = format!("{}", ty).to_pascal_case().to_plural();
let singular_ident = Ident::new(&singular_name, ty.span());
let plural_ident = Ident::new(&plural_name, ty.span());
if functional {
quote! {
#singular_ident(Box<#ty>)
}
} else {
quote! {
#singular_ident(Box<#ty>),
#plural_ident(HashMap<String, #ty>)
}
}
});
let expanded = quote! {
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(untagged)]
#visibility enum #name {
#(#variants,)*
}
};
expanded.into()
}