mod parse;
use darling::FromDeriveInput;
use proc_macro::{self, TokenStream};
use quote::quote;
use syn::parse_macro_input;
use parse::ModelInput;
#[proc_macro_derive(Model, attributes(table, column))]
pub fn derive(input: TokenStream) -> TokenStream {
let opts = ModelInput::from_derive_input(&parse_macro_input!(input)).unwrap();
let ident = opts.ident();
let table_name = opts.table_name();
let fields = opts.fields();
if opts.n_fields() == 0 {
panic!("struct must have at least one field to become a `Model`")
}
let field_idents = fields
.map(|f| f.clone().ident()).collect::<Vec<_>>();
let column_fields = opts.column_fields().collect::<Vec<_>>();
let column_counter = (1..=column_fields.len()).map(|i| format!("${i}")).collect::<Vec<_>>();
let column_counter_one = column_counter.join(", ");
let column_names = column_fields.clone().into_iter().map(|f| f.column_name()).collect::<Vec<_>>();
let column_names_one = column_names.join(", ");
let column_field_idents = column_fields.clone().into_iter().map(|f| f.ident()).collect::<Vec<_>>();
let column_field_types = column_fields.clone().into_iter().map(|f| f.ty()).collect::<Vec<_>>();
let create_table_sql = opts.get_create_sql();
let output = quote!(
impl<'a> TryFrom<&'a pg_worm::Row> for #ident {
type Error = pg_worm::pg::Error;
fn try_from(value: &'a pg_worm::Row) -> Result<#ident, Self::Error> {
Ok(#ident {
#(#field_idents: value.try_get(stringify!(#field_idents))?),*
})
}
}
#[pg_worm::async_trait]
impl Model<#ident> for #ident {
fn _create_table_sql() -> &'static str {
#create_table_sql
}
async fn select() -> Vec<#ident> {
let client = pg_worm::_get_client().expect("not connected to db");
let rows = client.query(format!("SELECT * FROM {}", #table_name).as_str(), &[]).await.unwrap();
rows.iter().map(|r| #ident::try_from(r).expect("couldn't parse data")).collect()
}
async fn select_one() -> Option<#ident> {
let client = pg_worm::_get_client().expect("not connected to db");
let rows = client.query(format!("SELECT * FROM {} LIMIT 1", #table_name).as_str(), &[]).await.unwrap();
if rows.len() != 1 {
return None;
}
Some(#ident::try_from(&rows[0]).unwrap())
}
}
impl #ident {
pub async fn insert(
#(#column_field_idents: #column_field_types),*
) -> Result<(), pg_worm::Error> {
let stmt = format!(
"INSERT INTO {} ({}) VALUES ({})",
#table_name,
#column_names_one,
#column_counter_one
);
let client = pg_worm::_get_client()?;
client.execute(
stmt.as_str(),
&[
#(&#column_field_idents), *
]
).await?;
Ok(())
}
}
);
output.into()
}