toql_derive/to_tokens/
tree_insert.rs

1use crate::parsed::{
2    field::{
3        field_kind::FieldKind,
4        join_field::JoinSelection,
5        merge_field::MergeSelection,
6        regular_field::{RegularSelection, SqlTarget},
7    },
8    parsed_struct::ParsedStruct,
9};
10use proc_macro2::TokenStream;
11
12pub(crate) fn to_tokens(parsed_struct: &ParsedStruct, tokens: &mut TokenStream) {
13    let mut insert_columns_code = Vec::new(); // For fields
14    let mut insert_values_code = Vec::new();
15
16    let mut dispatch_columns_code = Vec::new(); // for joins
17    let mut dispatch_values_code = Vec::new();
18
19    for field in &parsed_struct.fields {
20        if field.skip_mut {
21            continue;
22        }
23        let field_name_ident = &field.field_name;
24        let field_base_type = &field.field_base_type;
25        let field_query_name = &field.toql_query_name;
26
27        match &field.kind {
28            FieldKind::Skipped => {}
29            FieldKind::Regular(regular_kind) => {
30                if regular_kind.key && parsed_struct.auto_key {
31                    continue;
32                }
33                match regular_kind.sql_target {
34                    SqlTarget::Column(ref sql_column) => insert_columns_code.push(quote!(
35                                e.push_literal(#sql_column);
36                                e.push_literal(", ");
37                    )),
38                    SqlTarget::Expression(_) => {
39                        continue;
40                    }
41                }
42                insert_values_code.push( match regular_kind.selection {
43                    RegularSelection::SelectNullable => {
44                        // Option<Option<T>> (toql selectable of nullable column)
45                        quote!(
46                             if  let Some(field) = &self . #field_name_ident  {
47                                 values.push_arg(toql::sql_arg::SqlArg::from(field.as_ref()));
48                                 values.push_literal(", ");
49                             } else {
50                                values.push_literal("DEFAULT, ");
51                             }
52                        )
53                    }
54                    RegularSelection::PreselectNullable=> {
55                        // Option<T>  selected (nullable column)
56                        quote!(
57                              values.push_arg( toql::sql_arg::SqlArg::from(self . #field_name_ident.as_ref()));
58                              values.push_literal(", ");
59                        )
60                    }
61                    RegularSelection::Select => {
62                        // Option<T>  (toql selectable)
63                        quote!(
64                            if  let Some(field) = &self . #field_name_ident {
65                                 values.push_arg( toql::sql_arg::SqlArg::from(field));
66                                   values.push_literal(", ");
67                            } else {
68                                  values.push_literal("DEFAULT, ");
69                            }
70                        )
71                    }
72                    RegularSelection::Preselect => {
73                        // selected field
74                        quote!(
75                            values.push_arg(toql::sql_arg::SqlArg::from(&self . #field_name_ident));
76                            values.push_literal(", ");
77                        )
78                    }
79                });
80            }
81
82            FieldKind::Join(join_kind) => {
83                if join_kind.key && parsed_struct.auto_key {
84                    continue;
85                }
86
87                dispatch_columns_code.push(quote!(
88                        #field_query_name => {
89                            return Ok(<#field_base_type as toql::tree::tree_insert::TreeInsert>::columns(descendents)?);
90                        }
91                ));
92
93                dispatch_values_code.push(
94                   match join_kind.selection  {
95                                JoinSelection::SelectLeft => {
96                                    // Option<Option<T>
97                                    quote!(
98                                    #field_query_name => {
99                                         if let Some(f) = self. #field_name_ident .as_ref() {
100                                              if let Some(f) = f .as_ref() {
101                                                toql::tree::tree_insert::TreeInsert::values(f, descendents, roles, should_insert, values)?
102                                            }
103                                         }
104                                    }
105                                        ) },
106                                JoinSelection::SelectInner | JoinSelection::PreselectLeft => {
107                                    // Option<T>
108                                    quote!(
109                                    #field_query_name => {
110                                        if let Some(f) = self. #field_name_ident .as_ref() {
111                                            toql::tree::tree_insert::TreeInsert::values(f, descendents, roles, should_insert, values)?
112                                        }
113                                    }
114                                        ) },
115                                 JoinSelection::PreselectInner  => {
116                                     // T
117                                    quote!(
118                                        #field_query_name => {
119                                           toql::tree::tree_insert::TreeInsert::
120                                            values(& self. #field_name_ident, descendents, roles, should_insert, values)?
121                                        }
122                                    )}
123                   }
124               );
125                let columns_map_code = &join_kind.columns_map_code;
126                let default_self_column_code = &join_kind.default_self_column_code;
127
128                // Add if columns should not be skipped
129
130                if !join_kind.partial_table {
131                    insert_columns_code.push(quote!(
132                        for other_column in <<#field_base_type as toql::keyed::Keyed>::Key as toql::key::Key>::columns() {
133                                #default_self_column_code;
134                                let self_column = #columns_map_code;
135                                e.push_literal(self_column);
136                                e.push_literal(", ");
137                        }
138                    ));
139
140                    insert_values_code.push(
141                        match join_kind.selection  {
142                                    JoinSelection::SelectLeft => { // Option<Option<T>>
143                                            quote!(
144                                                if let Some(field) = &self. #field_name_ident {
145                                                    if let Some(f) = field {
146                                                        toql :: key :: Key :: params(&  toql :: keyed :: Keyed  :: key(f))
147                                                                                        .iter()
148                                                                                        .for_each(|p| {
149                                                                                            values.push_arg(p.to_owned());
150                                                                                            values.push_literal(", ");
151                                                                                            });
152                                                    } else {
153                                                        <<#field_base_type as toql::keyed::Keyed>::Key as toql::key::Key>::columns()
154                                                        .iter().for_each(|_| {
155                                                                values.push_arg(toql::sql_arg::SqlArg::Null);
156                                                                values.push_literal(", ");});
157
158                                                    }
159                                                } else {
160                                                    <<#field_base_type as toql::keyed::Keyed>::Key as toql::key::Key>::columns().iter().for_each(|_| { values.push_literal("DEFAULT, ");});
161                                                }
162
163                                            )
164                                        },
165                                    JoinSelection::PreselectLeft => { // #[toql(preselect)] Option<T> 
166                                    // TODO Option wrapping
167                                        quote!(
168                                            if let Some(f) =  &self. #field_name_ident {
169                                                        toql :: key :: Key :: params(& toql :: keyed :: Keyed  :: key(f))
170                                                                                        .iter()
171                                                                                        .for_each(|p| {
172                                                                                            values.push_arg(p.to_owned());
173                                                                                            values.push_literal(", ");
174                                                                                            });
175                                                    } else {
176                                                        <<#field_base_type as toql::keyed::Keyed>::Key as toql::key::Key>::columns()
177                                                        .iter().for_each(|_| {
178                                                                values.push_arg(toql::sql_arg::SqlArg::Null);
179                                                                values.push_literal(", ");
180                                                                });
181                                                    }
182                                            )
183                                    },
184
185                                    JoinSelection::SelectInner => { // Option<T> selectable 
186                                        quote!(
187                                            if let Some(field) = &self. #field_name_ident {
188                                                        toql :: key :: Key :: params(& toql :: keyed :: Keyed :: key(field))
189                                                                                        .iter()
190                                                                                        .for_each(|p| {
191                                                                                            values.push_arg(p.to_owned());
192                                                                                            values.push_literal(", ");
193                                                                                            });
194                                            } else {
195                                                <<#field_base_type as toql::keyed::Keyed>::Key as toql::key::Key>::columns()
196                                                    .iter().for_each(|_|  {values.push_literal("DEFAULT, ");});
197                                            }
198                                        )
199                                    },
200                                    JoinSelection::PreselectInner => { // T
201                                        quote!(
202                                            &toql::key::Key::params( &toql::keyed::Keyed::key(&self. #field_name_ident))
203                                        .into_iter() .for_each(|a| {values.push_arg(a); values.push_literal(", " );});
204                                    )
205                                    }
206                        }
207                    );
208                }
209            }
210            FieldKind::Merge(merge_kind) => {
211                dispatch_columns_code.push(
212                   quote!(
213                        #field_query_name => {
214                             return Ok(<#field_base_type as toql::tree::tree_insert::TreeInsert>::columns( descendents)?);
215                        }
216                )
217               );
218                dispatch_values_code.push(
219                    match merge_kind.selection {
220                        MergeSelection::Preselect => {
221                            // Vec<T>
222                            quote!(
223                                #field_query_name => {
224                                    for f in &self. #field_name_ident{
225                                        toql::tree::tree_insert::TreeInsert::values(f, descendents.clone(), roles, should_insert, values)?
226                                    }
227                                }
228                            )
229                        }
230                         MergeSelection::Select => {
231                             // Option<Vec<T>>
232                             quote!(
233                                #field_query_name => {
234                                    if let Some (fs) = self. #field_name_ident .as_ref(){
235                                        for f in fs {
236                                            toql::tree::tree_insert::TreeInsert::values(f,  descendents.clone(), roles, should_insert, values)?
237                                        }
238                                    }
239                                }
240                        )
241                        },
242                    }
243               );
244            }
245        };
246    }
247    let struct_name_ident = &parsed_struct.struct_name;
248    let struct_name = parsed_struct.struct_name.to_string();
249    let role_assert = if let Some(role_expr_string) = &parsed_struct.roles.insert {
250        quote!(
251            let role_expr = toql::role_expr_macro::role_expr!(#role_expr_string);
252            if !toql::role_validator::RoleValidator::is_valid(roles, &role_expr)  {
253                return Err( toql::sql_builder::sql_builder_error::SqlBuilderError::RoleRequired(role_expr.to_string(), format!("mapper `{}`", #struct_name) ).into())
254            }
255        )
256    } else {
257        quote!()
258    };
259
260    let mods = quote! {
261            impl toql::tree::tree_insert::TreeInsert for #struct_name_ident {
262
263                #[allow(unused_mut)]
264                fn columns<'a, I>(  mut descendents: I)
265                        -> std::result::Result<toql::sql_expr::SqlExpr, toql::error::ToqlError>
266                         where I: Iterator<Item = toql::query::field_path::FieldPath<'a>>
267                        {
268
269                    let mut e = toql::sql_expr::SqlExpr::new();
270                     match descendents.next() {
271                           Some(d) => match d.as_str() {
272                               #(#dispatch_columns_code),*
273                               f @ _ => {
274                                    return Err(
275                                        toql::sql_builder::sql_builder_error::SqlBuilderError::FieldMissing(f.to_string()).into());
276                                }
277                           },
278                           None => {
279                               e.push_literal("(");
280                               #(#insert_columns_code)*
281                               e.pop_literals(2);
282                               e.push_literal(")");
283                           }
284                    }
285                    Ok(e)
286                }
287                #[allow(unused_mut, unused_variables)]
288                fn values<'a,'b, I, J>(&self,
289                                    mut descendents: I,
290                                    roles: &std::collections::HashSet<String>,
291                                     mut should_insert:  &mut J,
292                                     values:  &mut toql::sql_expr::SqlExpr
293                            ) -> std::result::Result<(),  toql::error::ToqlError>
294                             where I: Iterator<Item = toql::query::field_path::FieldPath<'a>> + Clone,
295                             J: Iterator<Item =&'b bool >
296                            {
297
298                                match descendents.next() {
299                                    Some(d) => match d.as_str() {
300                                        #(#dispatch_values_code),*
301                                        f @ _ => {
302                                                return Err(
303                                                    toql::sql_builder::sql_builder_error::SqlBuilderError::FieldMissing(f.to_string()).into());
304                                            }
305                                    },
306                                    None => {
307                                          if !*should_insert.next().unwrap_or(&false) {
308                                                return Ok(())
309                                            }
310
311                                        #role_assert
312
313                                        values.push_literal("(");
314                                        #(#insert_values_code)*
315                                        values.pop_literals(2);
316                                        values.push_literal("), ");
317                                    }
318                                }
319                                Ok(())
320                            }
321            }
322
323              impl toql::tree::tree_insert::TreeInsert for &#struct_name_ident {
324
325                #[allow(unused_mut)]
326                fn columns<'a, I>(  mut descendents: I)
327                        -> std::result::Result<toql::sql_expr::SqlExpr, toql::error::ToqlError>
328                         where I: Iterator<Item = toql::query::field_path::FieldPath<'a>> {
329                            <#struct_name_ident as toql::tree::tree_insert::TreeInsert>::columns(descendents)
330                        }
331                #[allow(unused_mut)]
332                 fn values<'a,'b, I, J>(&self,
333                                    mut descendents: I,
334                                    roles: &std::collections::HashSet<String>,
335                                     mut should_insert:  &mut J,
336                                     values:  &mut toql::sql_expr::SqlExpr
337                            ) -> std::result::Result<(),  toql::error::ToqlError>
338                             where I: Iterator<Item = toql::query::field_path::FieldPath<'a>> + Clone,
339                             J: Iterator<Item =&'b bool >
340                            {
341                                <#struct_name_ident as toql::tree::tree_insert::TreeInsert>::values(self, descendents, roles, should_insert, values)
342                            }
343              }
344              impl toql::tree::tree_insert::TreeInsert for &mut #struct_name_ident {
345
346                #[allow(unused_mut)]
347                fn columns<'a, I>(  mut descendents: I)
348                        -> std::result::Result<toql::sql_expr::SqlExpr, toql::error::ToqlError>
349                         where I: Iterator<Item = toql::query::field_path::FieldPath<'a>> {
350                            <#struct_name_ident as toql::tree::tree_insert::TreeInsert>::columns(descendents)
351                        }
352                #[allow(unused_mut)]
353                 fn values<'a,'b, I, J>(&self,
354                                    mut descendents: I ,
355                                    roles: &std::collections::HashSet<String>,
356                                     mut should_insert:  &mut J,
357                                     values:  &mut toql::sql_expr::SqlExpr
358                            ) -> std::result::Result<(),  toql::error::ToqlError>
359                             where I: Iterator<Item = toql::query::field_path::FieldPath<'a>> + Clone,
360                             J: Iterator<Item =&'b bool >
361                            {
362                                <#struct_name_ident as toql::tree::tree_insert::TreeInsert>::values(self, descendents, roles, should_insert, values)
363                            }
364              }
365
366
367    };
368
369    log::debug!("Source code for `{}`:\n{}", struct_name, mods.to_string());
370    tokens.extend(mods);
371}