easy-sql-macros 1.1.2

Easy SQL support library
Documentation
use ::{
    anyhow::{self, Context},
    quote::{ToTokens, quote},
    syn,
};

use easy_macros::{TokensBuilder, always_context, parse_macro_input};
use easy_sql_compilation_data::CompilationData;

use crate::{
    derive_components::{
        DATABASE_SETUP_FIELD_KEYS, DATABASE_SETUP_STRUCT_KEYS, supported_drivers,
        validate_sql_attribute_keys,
    },
    sql_crate,
};

#[always_context]
pub fn database_setup(item: proc_macro::TokenStream) -> anyhow::Result<proc_macro::TokenStream> {
    let item = parse_macro_input!(item as syn::ItemStruct);

    if let Some(error_tokens) = validate_sql_attribute_keys(
        &item,
        "DatabaseSetup",
        DATABASE_SETUP_STRUCT_KEYS,
        DATABASE_SETUP_FIELD_KEYS,
    ) {
        return Ok(error_tokens.into());
    }

    let sql_crate = sql_crate();
    let macro_support = quote! { #sql_crate::macro_support };

    let fields = match &item.fields {
        syn::Fields::Named(fields_named) => &fields_named.named,
        syn::Fields::Unnamed(fields_unnamed) => &fields_unnamed.unnamed,
        syn::Fields::Unit => anyhow::bail!("Unit struct is not supported"),
    };

    let compilation_data = CompilationData::load_in_macro()?;

    let supported_drivers = supported_drivers(&item, &compilation_data, false)?;

    let mut result = TokensBuilder::default();

    for driver in supported_drivers {
        let fields_mapped=fields.iter().enumerate().map(|(index,field)| {
            let field_name=field.ident.as_ref().map(|e|e.into_token_stream()).unwrap_or_else(||{
                quote! {
                    #index
                }
            });
            let field_type=&field.ty;

            let field_type_str=field_type.to_token_stream().to_string();

            let context=format!("Field `{}` with type `{}` of struct `{}` ",field_name, field_type_str, item.ident);

            quote! {
                <#field_type as #sql_crate::DatabaseSetup<#driver>>::setup(conn).await.with_context(#macro_support::context!(#context))?;
            }
        });

        let item_name = &item.ident;

        result.add(quote! {
            impl #sql_crate::DatabaseSetup<#driver> for #item_name {
                async fn setup(
                    conn: &mut (impl #sql_crate::EasyExecutor<#driver> + Send + Sync)
                ) -> #macro_support::Result<()> {
                    use #macro_support::Context;

                    #(
                        #fields_mapped
                    )*
                    Ok(())
                }
            }
        })
    }

    Ok(result.finalize().into())
}