kdb_macros/
lib.rs

1#![doc = include_str!("../README.MD")]
2
3extern crate proc_macro;
4
5use convert_case::Casing;
6use proc_macro::TokenStream;
7use quote::quote;
8use syn::{Data, DeriveInput, parse_macro_input};
9
10/// The KRQLQuery derive macro will create two structure QC and QC_ used to serialize
11/// following the format expected by krQL.
12///
13/// For instance the following structure:
14///  
15/// ```nocompile
16/// pub struct Test
17/// {
18///   pub print: Option<String>,
19///   #[serde(rename = "return")]
20///   pub return_value: Option<String>,
21/// }
22/// ```
23///
24/// Should be serialized as:
25/// ```yaml
26/// test:
27///   print: "a string"
28///   return: "an other string"
29/// ```
30///
31/// But with serde, it would be serialized as:
32/// print: "a string"
33/// return: "an other string"
34///
35/// To solve that problem, KRQLQuery generates two structures TestQC and TestQC_.
36/// TestQC_ is a copy of Test used for serialization, and TestQC is defined as:
37///
38/// ```nocompile
39/// struct TestQC
40/// {
41///   test: TestQC_
42/// }
43/// ```
44///
45/// Then we can define Test with the following:
46///
47/// ```nocompile
48/// #[derive(Clone, Default, KRQLQuery)]
49/// pub struct Test { ... }
50/// ```
51///
52/// That means that Test is serialized using the TestQC class into the proper structure for krQL.
53///
54/// serde(...) can be used on fields
55///
56/// ```nocompilation
57/// # use kdb_connection::KRQLQuery;
58/// #[derive(Clone, Default, KRQLQuery)]
59/// pub struct Test {
60///   #[serde(rename="some field")]
61///   some_field: String,
62///  }
63/// ```
64/// krql(key = "...", tag = "...") can be used to set the krQL key, otherwise default to snake case
65/// version of the class name. While tag is passed to serde, for enums.
66#[proc_macro_derive(KRQLQuery, attributes(krql, serde))]
67pub fn krql_query(input: TokenStream) -> TokenStream
68{
69    let input = parse_macro_input!(input as DeriveInput);
70    let name = input.ident;
71    let name_qc = proc_macro2::Ident::new(&format!("{}QC", name), proc_macro2::Span::call_site());
72    let name_qc_ = proc_macro2::Ident::new(&format!("{}QC_", name), proc_macro2::Span::call_site());
73    let mut key_str = name.to_string().to_case(convert_case::Case::Snake);
74    let name_cc = proc_macro2::Ident::new(key_str.as_str(), proc_macro2::Span::call_site());
75    let mut tag_str = None;
76    for attribute in input.attrs
77    {
78        if attribute.path().is_ident("krql")
79        {
80            attribute
81                .parse_nested_meta(|meta| {
82                    // this parses the `(`
83                    if meta.path.is_ident("key")
84                    {
85                        // this parses the `key`
86                        let value = meta.value()?; // this parses the `=`
87                        let s: syn::LitStr = value.parse()?; // this parses the value
88                        key_str = s.value();
89                        Ok(())
90                    }
91                    else if meta.path.is_ident("tag")
92                    {
93                        // this parses the `tag`
94                        let value = meta.value()?; // this parses the `=`
95                        let s: syn::LitStr = value.parse()?; // this parses the value
96                        tag_str = Some(s.value());
97                        Ok(())
98                    }
99                    else
100                    {
101                        Err(meta.error("unsupported attribute"))
102                    }
103                })
104                .unwrap();
105        }
106    }
107    let key = proc_macro2::Literal::string(key_str.as_str());
108    let serde_qc_ = match tag_str
109    {
110        Some(tag) =>
111        {
112            let tag = proc_macro2::Literal::string(tag.as_str());
113            quote! {
114              #[serde(tag = #tag)]
115            }
116        }
117        None => proc_macro2::TokenStream::new(),
118    };
119    let out = match input.data
120    {
121        Data::Enum(e) =>
122        {
123            let variants_iter = e.variants.iter();
124            let variants_elements_for_match: Vec<
125                syn::punctuated::Punctuated<syn::Ident, syn::Token![,]>,
126            > = e
127                .variants
128                .iter()
129                .map(|v| v.fields.iter().map(|f| f.ident.clone().unwrap()).collect())
130                .collect();
131            let variants_elements_iter_a = variants_elements_for_match.iter();
132            let variants_elements_iter_b = variants_elements_for_match.iter();
133            let variants_names_for_match: Vec<syn::Ident> =
134                e.variants.iter().map(|v| v.ident.clone()).collect();
135            let variants_names_iter_a = variants_names_for_match.iter();
136            let variants_names_iter_b = variants_names_for_match.iter();
137            quote! {
138              impl KRQLQuery for #name
139              {
140              }
141              #[derive(serde::Serialize)]
142              struct #name_qc
143              {
144                #[serde(rename = #key)]
145                pub #name_cc: #name_qc_
146              }
147
148              impl From<#name> for #name_qc
149              {
150                fn from(value: #name) -> Self
151                {
152                  Self {
153                     #name_cc: #name_qc_::from(value)
154                   }
155                }
156              }
157
158              #[skip_serializing_none]
159              #[derive(serde::Serialize)]
160              #serde_qc_
161              enum #name_qc_
162              {
163                #(
164                  #variants_iter,
165                )*
166              }
167
168              impl From<#name> for #name_qc_
169              {
170                fn from(value: #name) -> Self
171                {
172                  match value {
173                    #(
174                      #name ::#variants_names_iter_a { #variants_elements_iter_a } => #name_qc_ ::#variants_names_iter_b { #variants_elements_iter_b},
175                    )*
176                  }
177                }
178              }
179              impl serde::Serialize for #name
180              {
181                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
182                where
183                  S: serde::Serializer,
184                {
185                  let t: #name_qc = self.clone().into();
186                  t.serialize(serializer)
187                }
188              }
189            }
190        }
191        Data::Struct(s) =>
192        {
193            let fields_iter = s.fields.iter();
194            let fields_names_iter = s.fields.iter().map(|field| field.ident.as_ref().unwrap());
195            quote! {
196              impl KRQLQuery for #name
197              {
198              }
199              #[derive(serde::Serialize, serde::Deserialize)]
200              struct #name_qc
201              {
202                #[serde(rename = #key)]
203                pub #name_cc: #name_qc_
204              }
205
206              impl From<#name> for #name_qc
207              {
208                fn from(value: #name) -> Self
209                {
210                  Self { #name_cc: #name_qc_::from(value) }
211                }
212              }
213
214              #[skip_serializing_none]
215              #[derive(serde::Serialize, serde::Deserialize)]
216              #serde_qc_
217              struct #name_qc_
218              {
219                #(
220                  #fields_iter,
221                )*
222              }
223
224              impl From<#name> for #name_qc_
225              {
226                fn from(value: #name) -> Self
227                {
228                  Self {
229                    #(
230                      #fields_names_iter: value.#fields_names_iter,
231                    )*
232                  }
233                }
234              }
235
236              impl serde::Serialize for #name
237              {
238                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
239                where
240                  S: serde::Serializer,
241                {
242                  let t: #name_qc = self.clone().into();
243                  t.serialize(serializer)
244                }
245              }
246
247            }
248        }
249        _ => todo!(),
250    };
251    quote! {
252      #out
253
254      impl TryFrom<&#name> for crate::dbc::Query
255      {
256          type Error = crate::Error;
257          fn try_from(value: &#name) -> std::result::Result<Self, Self::Error>
258          {
259              let krql_query = serde_saphyr::to_string(value)?;
260              Ok(crate::dbc::Query::new(
261                  krql_query,
262                  Default::default(),
263                  crate::dbc::QueryType::KRQL,
264              ))
265          }
266      }
267
268      impl TryFrom<#name> for crate::dbc::Query
269      {
270          type Error = crate::Error;
271          fn try_from(value: #name) -> std::result::Result<Self, Self::Error>
272          {
273              (&value).try_into()
274          }
275      }
276    }
277    .into()
278}