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
#![allow(unused)]
use proc_macro::TokenStream;
use quote::{quote, ToTokens};
use syn::{parse_macro_input, DeriveInput, Field, Ident};
use utils::{extract_context_attr_value, get_struct_fields, get_type_name};
mod utils;
#[proc_macro_derive(FilterNodes, attributes(context))]
pub fn derives_filter_nodes(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
let fields = get_struct_fields(&ast);
let mut props: Vec<&Option<Ident>> = Vec::new(); let mut prop_opval_idents: Vec<&Ident> = Vec::new();
let mut props_opval_contexts: Vec<proc_macro2::TokenStream> = Vec::new();
for field in fields.named.iter() {
let type_name = get_type_name(field);
if type_name.starts_with("Option ") && type_name.contains("OpVal") {
if let Some(ident) = field.ident.as_ref() {
prop_opval_idents.push(ident);
let block = if let Some(ctx) = extract_context_attr_value(field) {
quote! {
Some(#ctx.to_string())
}
} else {
quote! {
None
}
};
props_opval_contexts.push(block);
}
} else {
props.push(&field.ident);
}
}
let ff_pushes = quote! {
#(
ff.push((stringify!(#props), self.#props.clone()).into());
)*
};
let ff_opt_pushes = quote! {
#(
if let Some(val) = self.#prop_opval_idents {
let node = modql::FilterNode {
context_path: #props_opval_contexts,
name: stringify!(#prop_opval_idents).to_string(),
opvals: val.0.into_iter().map(|n| n.into()).collect(),
};
nodes.push(node);
}
)*
};
let out_impl_into_filter_nodes = quote! {
impl modql::IntoFilterNodes for #struct_name {
fn filter_nodes(self, context: Option<String>) -> Vec<modql::FilterNode> {
let mut nodes = Vec::new();
#ff_opt_pushes
nodes
}
}
};
let out_into_filter_node = quote! {
impl From<#struct_name> for Vec<modql::FilterNode> {
fn from(val: #struct_name) -> Self {
modql::IntoFilterNodes::filter_nodes(val, None)
}
}
};
let out_into_op_groups = quote! {
impl From<#struct_name> for modql::OrGroups {
fn from(val: #struct_name) -> Self {
let nodes: Vec<modql::FilterNode> = val.into();
nodes.into()
}
}
};
let output = quote! {
#out_impl_into_filter_nodes
#out_into_filter_node
#out_into_op_groups
};
output.into()
}