use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::parse::{Parse, ParseStream};
use syn::{FnArg, ItemFn, Pat};
#[derive(Debug, Default)]
pub(crate) struct DbUnwrapArgs {
transaction_required: bool,
}
impl Parse for DbUnwrapArgs {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(DbUnwrapArgs::default());
}
let ident: Ident = input.parse()?;
match ident.to_string().to_lowercase().as_str() {
"transaction_required" => Ok(DbUnwrapArgs {
transaction_required: true,
}),
unknown => Err(syn::Error::new_spanned(
ident,
format!("Unknown argument: {unknown}"),
)),
}
}
}
pub(crate) fn db_unwrap_macro(args: DbUnwrapArgs, input: ItemFn) -> TokenStream {
let fn_attrs = &input.attrs;
let fn_vis = &input.vis;
let fn_sig = &input.sig;
let transaction_required = args.transaction_required;
let has_db_param = input.sig.inputs.iter().any(|arg| match arg {
FnArg::Typed(pat_type) => {
if let Pat::Ident(pat_ident) = &*pat_type.pat {
pat_ident.ident == "db"
} else {
false
}
}
_ => false,
});
if !has_db_param {
return syn::Error::new_spanned(
&fn_sig,
"Service query method must have a 'db: Option<&C>' parameter",
)
.to_compile_error()
.into();
}
let user_block = &input.block;
let expanded = quote! {
#(#fn_attrs)*
#fn_vis #fn_sig {
if let Some(db) = db {
#user_block
} else {
let db_conn = robotech::db::get_db_conn()?;
let db = db_conn.as_ref();
if #transaction_required {
let tx = begin_transaction(db).await?;
let db = &tx;
}
#user_block
}
}
};
TokenStream::from(expanded)
}