use crate::MetadataCache;
use crate::placeholder::Placeholder;
use itertools::Itertools;
use ormlite_attr::ColumnMeta;
use ormlite_attr::Ident;
use ormlite_attr::ModelMeta;
use ormlite_attr::TableMeta;
use ormlite_attr::{InnerType, Type};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use std::borrow::Cow;
pub fn generate_conditional_bind(c: &ColumnMeta) -> TokenStream {
let name = &c.ident;
if c.is_join() {
quote! {
if let Some(value) = self.#name {
q = q.bind(value._id());
}
}
} else if c.json {
if c.is_option() {
quote! {
if let Some(value) = self.#name {
q = q.bind(value.map(::ormlite::types::Json));
}
}
} else {
quote! {
if let Some(value) = self.#name {
q = q.bind(::ormlite::types::Json(value));
}
}
}
} else {
quote! {
if let Some(value) = self.#name {
q = q.bind(value);
}
}
}
}
fn ty_is_string(ty: &syn::Type) -> bool {
let p = match ty {
syn::Type::Path(p) => p,
_ => return false,
};
p.path.segments.last().map(|s| s.ident == "String").unwrap_or(false)
}
fn recursive_primitive_types_ty<'a>(ty: &'a Type, cache: &'a MetadataCache) -> Vec<Cow<'a, InnerType>> {
match ty {
Type::Option(ty) => recursive_primitive_types_ty(ty, cache),
Type::Vec(ty) => {
let inner = recursive_primitive_types_ty(ty, cache);
let inner = inner.into_iter().next().expect("Vec must have inner type");
let inner: InnerType = inner.into_owned();
vec![Cow::Owned(InnerType {
path: vec![],
ident: Ident::from("Vec"),
args: Some(Box::new(inner)),
})]
}
Type::Inner(p) => vec![Cow::Borrowed(p)],
Type::Join(j) => {
let joined = cache.get(&j.inner_type_name()).expect("Join type not found");
recursive_primitive_types(joined, cache)
}
}
}
fn recursive_primitive_types<'a>(table: &'a ModelMeta, cache: &'a MetadataCache) -> Vec<Cow<'a, InnerType>> {
table
.columns
.iter()
.map(|c| recursive_primitive_types_ty(&c.ty, cache))
.flatten()
.collect()
}
pub(crate) fn table_primitive_types<'a>(attr: &'a TableMeta, cache: &'a MetadataCache) -> Vec<Cow<'a, InnerType>> {
attr.columns
.iter()
.filter(|c| !c.skip)
.filter(|c| !c.json)
.map(|c| recursive_primitive_types_ty(&c.ty, cache))
.flatten()
.unique()
.collect()
}
pub fn from_row_bounds<'a>(
db: &dyn OrmliteCodegen,
attr: &'a TableMeta,
cache: &'a MetadataCache,
) -> impl Iterator<Item = TokenStream> + 'a {
let database = db.database_ts();
table_primitive_types(attr, cache).into_iter().map(move |ty| {
quote! {
#ty: ::ormlite::decode::Decode<'a, #database>,
#ty: ::ormlite::types::Type<#database>,
}
})
}
fn is_vec(p: &syn::Path) -> bool {
let Some(segment) = p.segments.last() else {
return false;
};
segment.ident == "Vec"
}
pub fn insertion_binding(c: &ColumnMeta) -> TokenStream {
let name = &c.ident;
if c.is_join() {
quote! {
q = q.bind(#name._id());
}
} else if c.json {
if c.is_option() {
quote! {
q = q.bind(model.#name.map(::ormlite::types::Json));
}
} else {
quote! {
q = q.bind(::ormlite::types::Json(model.#name));
}
}
} else {
quote! {
q = q.bind(model.#name);
}
}
}
pub trait OrmliteCodegen {
fn dialect_ts(&self) -> TokenStream;
fn database_ts(&self) -> TokenStream;
fn placeholder_ts(&self) -> TokenStream;
fn placeholder(&self) -> Placeholder;
fn row(&self) -> TokenStream;
}