use crate::metadata::column::ColumnMeta;
use crate::DeriveInputExt;
use crate::Ident;
use convert_case::{Case, Casing};
use structmeta::StructMeta;
use syn::{Attribute, DeriveInput, LitStr};
#[derive(Debug, Clone)]
pub struct TableMeta {
pub name: String,
pub ident: Ident,
pub columns: Vec<ColumnMeta>,
pub databases: Vec<String>,
pub pkey: Option<String>,
}
impl TableMeta {
pub fn new(ast: &DeriveInput, attrs: &[TableAttr]) -> Self {
let ident = &ast.ident;
let name = if let Some(value) = attrs.iter().find_map(|a| a.table.as_ref()) {
value.value()
} else {
ident.to_string().to_case(Case::Snake)
};
let mut columns = ColumnMeta::from_fields(ast.fields());
let mut pkey = columns
.iter()
.find(|&c| c.marked_primary_key)
.map(|c| c.clone())
.map(|c| c.name.clone());
if pkey.is_none() {
let candidates = sql::util::pkey_column_names(&name);
if let Some(c) = columns.iter_mut().find(|c| candidates.iter().any(|n| c.ident == n)) {
c.has_database_default = true;
pkey = Some(c.name.clone());
}
}
let databases = attrs.iter().flat_map(|d| &d.database).map(|d| d.value()).collect();
Self {
name,
ident: Ident::from(ident),
columns,
databases,
pkey,
}
}
pub fn from_derive(ast: &DeriveInput) -> Self {
let attr = TableAttr::from_attrs(&ast.attrs);
Self::new(ast, &attr)
}
pub fn all_fields(&self) -> impl Iterator<Item = &Ident> + '_ {
self.columns.iter().map(|c| &c.ident)
}
pub fn database_columns(&self) -> impl Iterator<Item = &ColumnMeta> + '_ {
self.columns
.iter()
.filter(|&c| !c.skip)
.filter(|&c| !c.is_join() || c.is_join_one())
}
pub fn many_to_one_joins(&self) -> impl Iterator<Item = &ColumnMeta> + '_ {
self.columns.iter().filter(|&c| c.is_join_one())
}
#[allow(dead_code)]
pub(crate) fn mock(name: &str, columns: Vec<ColumnMeta>) -> Self {
TableMeta {
name: name.to_string(),
ident: Ident::from(name.to_case(Case::Pascal)),
pkey: None,
columns,
databases: vec![],
}
}
}
#[derive(StructMeta)]
pub struct TableAttr {
pub table: Option<LitStr>,
pub insertable: Option<syn::Ident>,
pub insert: Option<LitStr>,
pub extra_derives: Option<Vec<syn::Ident>>,
pub returns: Option<LitStr>,
pub database: Option<LitStr>,
}
impl TableAttr {
pub fn from_attrs(attrs: &[Attribute]) -> Vec<Self> {
attrs
.iter()
.filter(|&a| a.path().is_ident("ormlite"))
.map(|a| a.parse_args().unwrap())
.collect()
}
}