georm-macros 0.2.1

Macro support for Georm. Not intended to be used directly.
Documentation
use quote::quote;

mod composite_keys;
mod defaultable_struct;
mod ir;
pub(crate) use ir::GeormField;
mod relationships;
mod traits;
pub(crate) use composite_keys::IdType;

fn extract_georm_field_attrs(ast: &mut syn::DeriveInput) -> deluxe::Result<Vec<GeormField>> {
    let syn::Data::Struct(s) = &mut ast.data else {
        return Err(syn::Error::new_spanned(
            ast,
            "Cannot apply to something other than a struct",
        ));
    };
    let fields = s
        .fields
        .clone()
        .into_iter()
        .map(|mut field| GeormField::new(&mut field))
        .collect::<Vec<GeormField>>();
    let identifiers: Vec<GeormField> = fields
        .clone()
        .into_iter()
        .filter(|field| field.id)
        .collect();
    if identifiers.is_empty() {
        Err(syn::Error::new_spanned(
            ast,
            "Struct {name} must have one identifier",
        ))
    } else {
        Ok(fields)
    }
}

pub fn georm_derive_macro2(
    item: proc_macro2::TokenStream,
) -> deluxe::Result<proc_macro2::TokenStream> {
    let mut ast: syn::DeriveInput = syn::parse2(item).expect("Failed to parse input");
    let struct_attrs: ir::GeormStructAttributes =
        deluxe::extract_attributes(&mut ast).expect("Could not extract attributes from struct");
    let fields = extract_georm_field_attrs(&mut ast)?;
    let defaultable_struct =
        defaultable_struct::derive_defaultable_struct(&ast, &struct_attrs, &fields);
    let from_row_impl = generate_from_row_impl(&ast, &fields);

    let (identifier, id_struct) = composite_keys::create_primary_key(&ast, &fields);

    let relationships =
        relationships::derive_relationships(&ast, &struct_attrs, &fields, &identifier);
    let trait_impl = traits::derive_trait(&ast, &struct_attrs.table, &fields, &identifier);

    let code = quote! {
        #id_struct
        #defaultable_struct
        #relationships
        #trait_impl
        #from_row_impl
    };
    Ok(code)
}

fn generate_from_row_impl(
    ast: &syn::DeriveInput,
    fields: &[GeormField],
) -> proc_macro2::TokenStream {
    let struct_name = &ast.ident;
    let field_idents: Vec<&syn::Ident> = fields.iter().map(|f| &f.ident).collect();
    let field_names: Vec<String> = fields.iter().map(|f| f.ident.to_string()).collect();

    quote! {
        impl<'r> ::sqlx::FromRow<'r, ::sqlx::postgres::PgRow> for #struct_name {
            fn from_row(row: &'r ::sqlx::postgres::PgRow) -> ::sqlx::Result<Self> {
                use ::sqlx::Row;
                Ok(Self {
                    #(#field_idents: row.try_get(#field_names)?),*
                })
            }
        }
    }
}