caretta_sync_macros/
lib.rs1mod derive;
2
3use derive::*;
4use heck::ToUpperCamelCase;
5use proc_macro::{self, TokenStream};
6use proc_macro2::Span;
7use quote::{ToTokens, format_ident, quote};
8use syn::{
9 Data, DataStruct, DeriveInput, Expr, ExprTuple, Field, Fields, FieldsNamed, Ident,
10 parse_macro_input,
11};
12
13fn extract_unique_field_ident<'a>(
14 fields: &'a FieldsNamed,
15 attribute_arg: &'static str,
16) -> &'a Ident {
17 let mut fields = extract_field_idents(fields, attribute_arg);
18 if fields.len() == 1 {
19 return fields.pop().unwrap();
20 } else {
21 panic!("Model must need one {} field attribute", attribute_arg);
22 };
23}
24
25fn extract_field_idents<'a>(
26 fields: &'a FieldsNamed,
27 attribute_arg: &'static str,
28) -> Vec<&'a Ident> {
29 fields
30 .named
31 .iter()
32 .filter_map(|field| {
33 field.attrs.iter().find_map(|attr| {
34 if attr.path().is_ident("syncable") {
35 let args: Expr = attr.parse_args().unwrap();
36
37 match args {
38 Expr::Tuple(arg_tupple) => arg_tupple.elems.iter().find_map(|arg| {
39 if let Expr::Path(arg_path) = arg {
40 if arg_path.path.is_ident(attribute_arg) {
41 Some(field.ident.as_ref().unwrap())
42 } else {
43 None
44 }
45 } else {
46 None
47 }
48 }),
49 Expr::Path(arg_path) => {
50 if arg_path.path.is_ident(attribute_arg) {
51 Some(field.ident.as_ref().unwrap())
52 } else {
53 None
54 }
55 }
56 _ => None,
57 }
58 } else {
59 None
60 }
61 })
62 })
63 .collect()
64}
65
66fn extract_fields(data: &Data) -> &FieldsNamed {
67 match *data {
68 Data::Struct(ref data) => match data.fields {
69 Fields::Named(ref fields) => fields,
70 _ => panic!("all fields must be named."),
71 },
72 _ => panic!("struct expected, but got other item."),
73 }
74}
75
76#[proc_macro_derive(Emptiable)]
77pub fn emptiable(input: TokenStream) -> TokenStream {
78 let input = parse_macro_input!(input as DeriveInput);
79 let type_ident = input.ident;
80 match input.data {
81 Data::Struct(ref data) => {
82 let field_idents = extract_idents_and_types_from_data_struct(data);
83 let is_empty_iter = field_idents.iter().map(|(ident, type_name)| {
84 quote! {
85 <#type_name as Emptiable>::is_empty(&self.#ident)
86 }
87 });
88 let empty_iter = field_idents.iter().map(|(ident, type_name)| {
89 quote! {
90 #ident: <#type_name as Emptiable>::empty(),
91 }
92 });
93 quote! {
94 impl Emptiable for #type_ident {
95 fn empty() -> Self {
96 Self {
97 #(#empty_iter)*
98 }
99 }
100 fn is_empty(&self) -> bool {
101 #(#is_empty_iter)&&*
102 }
103 }
104 }
105 .into()
106 }
107 _ => panic!("struct or expected, but got other type."),
108 }
109}
110
111#[proc_macro_derive(Mergeable)]
112pub fn mergeable(input: TokenStream) -> TokenStream {
113 let input = parse_macro_input!(input as DeriveInput);
114 let type_ident = input.ident;
115 match input.data {
116 Data::Struct(ref data) => {
117 let field_idents = extract_idents_and_types_from_data_struct(data);
118 let merge_iter = field_idents.iter().map(|(ident, type_name)| {
119 quote! {
120 <#type_name as Mergeable>::merge(&mut self.#ident, other.#ident);
121 }
122 });
123 quote! {
124 impl Mergeable for #type_ident {
125 fn merge(&mut self, mut other: Self){
126 #(#merge_iter)*
127 }
128 }
129 }
130 .into()
131 }
132 _ => panic!("struct expected, but got other type."),
133 }
134}
135
136#[proc_macro_derive(Runnable, attributes(runnable))]
137pub fn runnable(input: TokenStream) -> TokenStream {
138 let input = parse_macro_input!(input as DeriveInput);
139 let type_ident = input.ident;
140 match input.data {
141 Data::Struct(ref data) => {
142 let mut idents =
143 extract_idents_and_types_from_data_struct_with_attribute(data, "runnable");
144 let (field_ident, field_type) = unwrap_vec_or_panic(
145 idents,
146 "Runnable struct must have one field with runnable attribute",
147 );
148
149 quote! {
150 impl Runnable for #type_ident {
151 fn run(self, app_name: &'static str) {
152 <#field_type as Runnable>::run(self.#field_ident, app_name)
153 }
154 }
155 }
156 .into()
157 }
158 Data::Enum(ref variants) => {
159 let quote_vec = extract_idents_and_types_from_enum_struct(&variants);
160 let quote_iter = quote_vec.iter().map(|(variant_ident, variant_type)| {
161 quote! {
162 Self::#variant_ident(x) => <#variant_type as Runnable>::run(x, app_name),
163 }
164 });
165 quote! {
166 impl Runnable for #type_ident {
167 fn run(self, app_name: &'static str) {
168 match self {
169 #(#quote_iter)*
170 }
171 }
172 }
173 }
174 .into()
175 }
176 _ => panic!("struct or enum expected, but got union."),
177 }
178}