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