rdb_pagination_derive/
lib.rs1mod common;
8mod panic;
9
10use common::{meta_2_string, Join};
11use proc_macro::TokenStream;
12use quote::quote;
13use rdb_pagination_core::{Name, OrderBuilder, Relationship};
14use syn::{
15 parse::{Parse, ParseStream},
16 parse_macro_input,
17 punctuated::Punctuated,
18 spanned::Spanned,
19 Data, DeriveInput, Index, Meta, Token,
20};
21
22use crate::common::OrderByOption;
23
24fn derive_input_handler(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
25 let mut table_name = None;
26 let mut join_list = Vec::new();
27
28 for attr in ast.attrs.iter() {
29 let path = attr.path();
30
31 if path.is_ident("orderByOptions") {
32 if let Meta::List(list) = &attr.meta {
33 let result =
34 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
35
36 for meta in result {
37 let path = meta.path();
38
39 if let Some(ident) = path.get_ident() {
40 match ident.to_string().as_str() {
41 "name" => {
42 if table_name.is_some() {
43 return Err(syn::Error::new(
44 ident.span(),
45 "`name` has been set",
46 ));
47 }
48
49 let name = meta_2_string(&meta)?;
50
51 table_name = Some(name);
52 },
53 "join" => {
54 if let Meta::List(list) = meta {
55 let join: Join = list.parse_args()?;
56
57 join_list.push(join);
58 } else {
59 return Err(syn::Error::new(
60 ident.span(),
61 "`join` should be a list",
62 ));
63 }
64 },
65 _ => {
66 return Err(panic::sub_attributes_for_item(path.span()));
67 },
68 }
69 } else {
70 return Err(panic::sub_attributes_for_item(path.span()));
71 }
72 }
73 } else {
74 return Err(syn::Error::new(
75 path.span(),
76 "the `orderByOptions` attribute should be a list",
77 ));
78 }
79 }
80 }
81
82 let mut token_stream = proc_macro2::TokenStream::new();
83
84 if let Some(table_name) = table_name {
85 let mut relationship = Relationship::new(Name::Dynamic(table_name.clone()));
86
87 for join in join_list.iter() {
88 if let Err(error) = relationship.join_check(
89 join.foreign.clone(),
90 join.primary.clone(),
91 join.real_table_name.clone(),
92 ) {
93 return Err(syn::Error::new(join.span, error));
94 }
95 }
96
97 if let Data::Struct(data) = ast.data {
98 let mut options = Vec::with_capacity(data.fields.len());
99
100 {
101 let mut order_builder: OrderBuilder<i16> =
102 OrderBuilder::new(relationship, data.fields.len());
103
104 for (index, field) in data.fields.iter().enumerate() {
105 let mut has_option = false;
106
107 for attr in field.attrs.iter() {
108 let path = attr.path();
109
110 if path.is_ident("orderByOptions") {
111 if has_option {
112 return Err(syn::Error::new(
113 path.span(),
114 "`orderByOptions` has been set",
115 ));
116 }
117
118 let order_by_option: OrderByOption = attr.parse_args()?;
119
120 if let Err(error) = order_builder.add_order_option_check(
121 order_by_option.table_column.clone(),
122 order_by_option.unique,
123 ) {
124 return Err(syn::Error::new(order_by_option.span, error));
125 }
126
127 has_option = true;
128
129 options.push((index, field, order_by_option));
130 }
131 }
132 }
133 }
134 let name = &ast.ident;
136
137 let options_len = options.len();
138
139 if options_len == 0 {
140 token_stream.extend(quote! {
141 impl OrderByOptions for #name {}
142 });
143 } else {
144 let mut join_impl = proc_macro2::TokenStream::new();
145
146 for join in join_list {
147 let foreign_table_name = join.foreign.0.as_ref();
148 let foreign_column_name = join.foreign.1.as_ref();
149 let primary_table_name = join.primary.0.as_ref();
150 let primary_column_name = join.primary.1.as_ref();
151
152 let real_table_name = if let Some(real_table_name) = join.real_table_name {
153 let real_table_name = real_table_name.as_ref();
154
155 quote!(Some(rdb_pagination_prelude::Name::Static(#real_table_name)))
156 } else {
157 quote!(None)
158 };
159
160 join_impl.extend(quote! {
161 relationship.join(
162 (rdb_pagination_prelude::Name::Static(#foreign_table_name), rdb_pagination_prelude::Name::Static(#foreign_column_name)),
163 (rdb_pagination_prelude::Name::Static(#primary_table_name), rdb_pagination_prelude::Name::Static(#primary_column_name)),
164 #real_table_name
165 );
166 });
167 }
168
169 let mut options_impl = proc_macro2::TokenStream::new();
170
171 for (index, field, option) in options {
172 let table_name = option.table_column.0.as_ref();
173 let column_name = option.table_column.1.as_ref();
174 let unique = option.unique;
175
176 let null_strategy =
177 if let Some(nulls_first_or_last) = option.nulls_first_or_last {
178 if nulls_first_or_last {
179 quote!(rdb_pagination_prelude::NullStrategy::First)
180 } else {
181 quote!(rdb_pagination_prelude::NullStrategy::Last)
182 }
183 } else {
184 quote!(rdb_pagination_prelude::NullStrategy::Default)
185 };
186
187 let order_method = if let Some(ident) = &field.ident {
188 quote!(self.#ident)
189 } else {
190 let index = Index::from(index);
191
192 quote!(self.#index)
193 };
194
195 options_impl.extend(quote! {
196 order_builder.add_order_option(
197 (rdb_pagination_prelude::Name::Static(#table_name), rdb_pagination_prelude::Name::Static(#column_name)),
198 #unique,
199 #null_strategy,
200 #order_method,
201 );
202 });
203 }
204
205 let order_by_options_impl = quote! {
206 impl OrderByOptions for #name {
207 fn to_sql(&self) -> (::std::vec::Vec<rdb_pagination_prelude::SqlJoin>, ::std::vec::Vec<rdb_pagination_prelude::SqlOrderByComponent>) {
208 let mut relationship = rdb_pagination_prelude::Relationship::new(rdb_pagination_prelude::Name::Static(#table_name));
209
210 #join_impl
211
212 let mut order_builder = rdb_pagination_prelude::OrderBuilder::new(relationship, #options_len);
213
214 #options_impl
215
216 order_builder.build()
217 }
218 }
219 };
220
221 token_stream.extend(order_by_options_impl);
222 }
223 } else {
224 return Err(syn::Error::new(
225 ast.ident.span(),
226 "should use a struct to implement `OrderByOptions`",
227 ));
228 }
229 }
230
231 Ok(token_stream)
232}
233
234#[proc_macro_derive(OrderByOptions, attributes(orderByOptions))]
235pub fn order_by_options_derive(input: TokenStream) -> TokenStream {
236 struct MyDeriveInput(proc_macro2::TokenStream);
237
238 impl Parse for MyDeriveInput {
239 #[inline]
240 fn parse(input: ParseStream) -> syn::Result<Self> {
241 let token_stream = derive_input_handler(input.parse::<DeriveInput>()?)?;
242
243 Ok(Self(token_stream))
244 }
245 }
246
247 let derive_input = parse_macro_input!(input as MyDeriveInput);
249
250 derive_input.0.into()
251}