use syn::{Data, DeriveInput, Fields};
use super::attrs::{ColumnAttr, ComputedAttr, IgnoreAttr, PkAttr, ServerDefaultAttr, TableAttr};
pub struct FieldInfo {
pub rust_name: String,
pub sql_name: String,
pub ty: syn::Type,
pub is_pk: bool,
pub pk_is_identity: bool,
pub is_ignored: bool,
pub is_computed: bool,
pub is_server_default: bool,
}
pub struct StructInfo {
pub struct_name: syn::Ident,
pub table_name: String,
pub schema: String,
pub fields: Vec<FieldInfo>,
}
impl StructInfo {
pub fn parse(input: &DeriveInput) -> syn::Result<Self> {
let table_attr = TableAttr::from_attrs(&input.attrs);
let table_name = table_attr
.table_name
.clone()
.unwrap_or_else(|| input.ident.to_string());
let schema = table_attr.schema.clone().unwrap_or_else(|| "dbo".to_string());
let named_fields = match &input.data {
Data::Struct(ds) => match &ds.fields {
Fields::Named(f) => &f.named,
_ => return Err(syn::Error::new_spanned(&input.ident, "SqlEntity requires named fields")),
},
_ => return Err(syn::Error::new_spanned(&input.ident, "SqlEntity can only be derived on structs")),
};
let mut fields = Vec::new();
for field in named_fields {
let rust_name = field.ident.as_ref().unwrap().to_string();
let ignore = IgnoreAttr::from_attrs(&field.attrs);
let pk = PkAttr::from_attrs(&field.attrs);
let col = ColumnAttr::from_attrs(&field.attrs);
let computed = ComputedAttr::from_attrs(&field.attrs);
let default = ServerDefaultAttr::from_attrs(&field.attrs);
let sql_name = col.column_name.clone().unwrap_or_else(|| rust_name.clone());
fields.push(FieldInfo {
rust_name,
sql_name,
ty: field.ty.clone(),
is_pk: field.attrs.iter().any(|a| a.path().is_ident("sql_primary_key")),
pk_is_identity: pk.is_identity,
is_ignored: ignore.ignore,
is_computed: computed.is_computed,
is_server_default: default.is_server_default,
});
}
let has_pk = fields.iter().any(|f| f.is_pk);
if !has_pk {
return Err(syn::Error::new_spanned(
&input.ident,
"SqlEntity requires exactly one field marked with \
`#[sql_primary_key]` or `#[sql_primary_key(identity)]`. \
Add the attribute to your primary-key field.",
));
}
Ok(StructInfo {
struct_name: input.ident.clone(),
table_name,
schema,
fields,
})
}
pub fn active_fields(&self) -> impl Iterator<Item = &FieldInfo> {
self.fields.iter().filter(|f| !f.is_ignored)
}
pub fn insert_fields(&self) -> impl Iterator<Item = &FieldInfo> {
self.fields.iter().filter(|f| {
!f.is_ignored && !(f.is_pk && f.pk_is_identity) && !f.is_computed && !f.is_server_default
})
}
pub fn update_fields(&self) -> impl Iterator<Item = &FieldInfo> {
self.fields.iter().filter(|f| !f.is_ignored && !f.is_pk && !f.is_computed)
}
pub fn pk_field(&self) -> Option<&FieldInfo> {
self.fields.iter().find(|f| f.is_pk)
}
}