use crate::codegen::common::{from_row_bounds, OrmliteCodegen};
use crate::MetadataCache;
use ormlite_attr::Ident;
use ormlite_attr::TableMeta;
use ormlite_attr::{ColumnMeta, Type};
use proc_macro2::TokenStream;
use quote::quote;
pub fn impl_FromRow(db: &dyn OrmliteCodegen, attr: &TableMeta, cache: &MetadataCache) -> TokenStream {
let bounds = from_row_bounds(db, attr, cache);
let row = db.row();
let prefix_branches = attr.columns.iter().filter(|&c| c.is_join_one()).map(|c| {
let name = &c.ident.to_string();
let iden = &c.ident;
let meta = cache
.get(c.joined_struct_name().unwrap().as_str())
.expect("Joined struct not found");
let prefixed_columns = meta.database_columns().map(|c| format!("__{}__{}", iden, c.ident));
let path = c.joined_model();
let result = quote! {
#path::from_row_using_aliases(row, &[
#(
#prefixed_columns,
)*
])?
};
quote! {
#name => {
model.#iden = ::ormlite::model::Join::_query_result(#result);
}
}
});
let field_names = attr.database_columns().map(|c| &c.name);
let map_join = if attr.columns.iter().any(|c| c.is_join()) {
quote! {
let mut prefixes = ::ormlite::Row::columns(row).iter().filter_map(|c| {
let name = ::ormlite::Column::name(c);
if name.starts_with("__") {
name.rsplitn(2, "__").last().map(|s| &s[2..])
} else {
None
}
})
.collect::<Vec<_>>();
prefixes.sort();
prefixes.dedup();
for prefix in prefixes {
match prefix {
#(
#prefix_branches
)*
_ => {
return Err(::ormlite::SqlxError::Decode(
Box::new(::ormlite::Error::OrmliteError(format!("Unknown column prefix: {}", prefix))),
));
}
}
}
}
} else {
TokenStream::new()
};
let model = &attr.ident;
quote! {
impl<'a> ::ormlite::model::FromRow<'a, #row> for #model
where
#(
#bounds
)*
{
fn from_row(row: &'a #row) -> ::std::result::Result<Self, ::ormlite::SqlxError> {
#[allow(unused_mut)]
let mut model = Self::from_row_using_aliases(row, &[
#(
#field_names,
)*
])?;
#map_join
Ok(model)
}
}
}
}
pub fn impl_from_row_using_aliases(
db: &dyn OrmliteCodegen,
attr: &TableMeta,
metadata_cache: &MetadataCache,
) -> TokenStream {
let row = db.row();
let fields = attr.all_fields();
let bounds = from_row_bounds(db, attr, &metadata_cache);
let mut incrementer = 0usize..;
let columns = attr
.columns
.iter()
.map(|c| {
if c.skip {
let id = &c.ident;
return quote! {
let #id = Default::default();
};
}
let index = incrementer.next().unwrap();
let get = quote! { aliases[#index] };
from_row_for_column(get, c)
})
.collect::<Vec<_>>();
let model = &attr.ident;
quote! {
impl #model {
pub fn from_row_using_aliases<'a>(row: &'a #row, aliases: &'a [&str]) -> ::std::result::Result<Self, ::ormlite::SqlxError>
where
#(
#bounds
)*
{
#(
#columns
)*
Ok(Self { #(#fields,)* })
}
}
}
}
pub fn from_row_for_column(get_value: TokenStream, col: &ColumnMeta) -> TokenStream {
let id = &col.ident;
let ty = &col.ty;
if col.is_join() {
let id_id = Ident::from(format!("{}_id", id));
quote! {
let #id_id: <#ty as ::ormlite::model::JoinMeta>::IdType = ::ormlite::Row::try_get(row, #get_value)?;
let #id = ::ormlite::model::Join::new_with_id(#id_id);
}
} else if col.json {
if let Type::Option(inner) = ty {
quote! {
let #id: Option<::ormlite::types::Json<#inner>> = ::ormlite::Row::try_get(row, #get_value)?;
let #id = #id.map(|j| j.0);
}
} else {
quote! {
let #id: ::ormlite::types::Json<#ty> = ::ormlite::Row::try_get(row, #get_value)?;
let #id = #id.0;
}
}
} else {
quote! {
let #id: #ty = ::ormlite::Row::try_get(row, #get_value)?;
}
}
}