use proc_macro2::Span;
use quote::quote;
use syn::{parse2, DeriveInput, Fields};
pub(crate) fn handler(
input: proc_macro2::TokenStream,
) -> Result<proc_macro2::TokenStream, (Span, &'static str)> {
let derive_input =
parse2::<DeriveInput>(input).map_err(|_| (Span::call_site(), "Failed to parse input"))?;
let ident = &derive_input.ident;
let fields = match &derive_input.data {
syn::Data::Struct(data_struct) => match &data_struct.fields {
Fields::Named(named) => &named.named,
_ => return Err((derive_input.ident.span(), "Only named fields are supported")),
},
_ => return Err((derive_input.ident.span(), "Only structs are supported")),
};
let field_info: Vec<_> = fields
.iter()
.filter_map(|field| {
field
.ident
.as_ref()
.map(|ident| (ident.clone(), field.ty.clone()))
})
.collect();
let column_names: Vec<String> = field_info
.iter()
.map(|(ident, _)| ident.to_string())
.collect();
let field_idents: Vec<_> = field_info.iter().map(|(ident, _)| ident).collect();
let field_names: Vec<String> = field_info
.iter()
.map(|(ident, _)| ident.to_string())
.collect();
let ret = quote! {
impl ::conservator::Selectable for #ident {
const COLUMN_NAMES: &'static [&'static str] = &[#(#column_names),*];
}
impl<'r> ::sqlx::FromRow<'r, ::sqlx::postgres::PgRow> for #ident {
fn from_row(row: &'r ::sqlx::postgres::PgRow) -> Result<Self, ::sqlx::Error> {
use ::sqlx::Row;
Ok(Self {
#(#field_idents: row.try_get(#field_names)?),*
})
}
}
};
Ok(ret)
}