ormlite_macro/codegen/
common.rs

1use crate::MetadataCache;
2use itertools::Itertools;
3use ormlite_attr::ColumnMeta;
4use ormlite_attr::Ident;
5use ormlite_attr::ModelMeta;
6use ormlite_attr::TableMeta;
7use ormlite_attr::{InnerType, Type};
8use ormlite_core::query_builder::Placeholder;
9use proc_macro2::TokenStream;
10use quote::{quote, ToTokens};
11use std::borrow::Cow;
12
13pub fn generate_conditional_bind(c: &ColumnMeta) -> TokenStream {
14    let name = &c.ident;
15    if c.is_join() {
16        quote! {
17            if let Some(value) = self.#name {
18                q = q.bind(value._id());
19            }
20        }
21    } else if c.json {
22        if c.is_option() {
23            quote! {
24                if let Some(value) = self.#name {
25                    q = q.bind(value.map(::ormlite::types::Json));
26                }
27            }
28        } else {
29            quote! {
30                if let Some(value) = self.#name {
31                    q = q.bind(::ormlite::types::Json(value));
32                }
33            }
34        }
35    } else {
36        quote! {
37            if let Some(value) = self.#name {
38                q = q.bind(value);
39            }
40        }
41    }
42}
43
44/// bool whether the given type is `String`
45fn ty_is_string(ty: &syn::Type) -> bool {
46    let p = match ty {
47        syn::Type::Path(p) => p,
48        _ => return false,
49    };
50    p.path.segments.last().map(|s| s.ident == "String").unwrap_or(false)
51}
52
53fn recursive_primitive_types_ty<'a>(ty: &'a Type, cache: &'a MetadataCache) -> Vec<Cow<'a, InnerType>> {
54    match ty {
55        Type::Option(ty) => recursive_primitive_types_ty(ty, cache),
56        Type::Vec(ty) => {
57            let inner = recursive_primitive_types_ty(ty, cache);
58            let inner = inner.into_iter().next().expect("Vec must have inner type");
59            let inner: InnerType = inner.into_owned();
60            vec![Cow::Owned(InnerType {
61                path: vec![],
62                ident: Ident::from("Vec"),
63                args: Some(Box::new(inner)),
64            })]
65        }
66        Type::Inner(p) => vec![Cow::Borrowed(p)],
67        Type::Join(j) => {
68            let joined = cache.get(&j.inner_type_name()).expect("Join type not found");
69            recursive_primitive_types(joined, cache)
70        }
71    }
72}
73
74fn recursive_primitive_types<'a>(table: &'a ModelMeta, cache: &'a MetadataCache) -> Vec<Cow<'a, InnerType>> {
75    table
76        .columns
77        .iter()
78        .map(|c| recursive_primitive_types_ty(&c.ty, cache))
79        .flatten()
80        .collect()
81}
82
83pub(crate) fn table_primitive_types<'a>(attr: &'a TableMeta, cache: &'a MetadataCache) -> Vec<Cow<'a, InnerType>> {
84    attr.columns
85        .iter()
86        .filter(|c| !c.skip)
87        .filter(|c| !c.json)
88        .map(|c| recursive_primitive_types_ty(&c.ty, cache))
89        .flatten()
90        .unique()
91        .collect()
92}
93
94pub fn from_row_bounds<'a>(
95    db: &dyn OrmliteCodegen,
96    attr: &'a TableMeta,
97    cache: &'a MetadataCache,
98) -> impl Iterator<Item = TokenStream> + 'a {
99    let database = db.database_ts();
100    table_primitive_types(attr, cache).into_iter().map(move |ty| {
101        quote! {
102            #ty: ::ormlite::decode::Decode<'a, #database>,
103            #ty: ::ormlite::types::Type<#database>,
104        }
105    })
106}
107
108fn is_vec(p: &syn::Path) -> bool {
109    let Some(segment) = p.segments.last() else {
110        return false;
111    };
112    segment.ident == "Vec"
113}
114
115/// Used to bind fields to the query upon insertion, update, etc.
116/// Assumed Bindings:
117/// - `model`: model struct
118/// - `q`: sqlx query
119pub fn insertion_binding(c: &ColumnMeta) -> TokenStream {
120    let name = &c.ident;
121    if c.is_join() {
122        quote! {
123            q = q.bind(#name._id());
124        }
125    } else if c.json {
126        if c.is_option() {
127            quote! {
128                q = q.bind(model.#name.map(::ormlite::types::Json));
129            }
130        } else {
131            quote! {
132                q = q.bind(::ormlite::types::Json(model.#name));
133            }
134        }
135    } else {
136        quote! {
137            q = q.bind(model.#name);
138        }
139    }
140}
141
142pub trait OrmliteCodegen {
143    fn dialect_ts(&self) -> TokenStream;
144    fn database_ts(&self) -> TokenStream;
145    fn placeholder_ts(&self) -> TokenStream;
146    // A placeholder that works at the phase when its invoked (e.g. during comp time, it can be used.
147    // Compare to placeholder_ts, which is just the tokens of a placeholder, and therefore can't be "used" until runtime.
148    fn placeholder(&self) -> Placeholder;
149    fn row(&self) -> TokenStream;
150}