postgres-named-parameters-derive 0.1.0

Derive macros used internally by `postgres-named-parameters`. You should not use this crate directly; instead, use `postgres-named-parameters`.
Documentation
use attribute_derive::FromAttr;
use quote::quote;
use syn::{DeriveInput, Type};

#[derive(FromAttr)]
#[attribute(ident = query)]
struct QueryTraitHelperAttribute {
    #[attribute(example = r#""SELECT * FROM Person WHERE first_name = @name""#)]
    sql: String,
    #[attribute(example = "crate::my_database_tables::Person")]
    row: Type,
}

pub fn derive_query_impl(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
    let args = QueryTraitHelperAttribute::from_attributes(&ast.attrs)?;
    let syn::Data::Struct(struct_ast) = ast.data else {
        return Err(syn::Error::new(
            ast.ident.span(),
            "#[derive(Query)] can only be used on structs",
        ));
    };
    let named_parameters = crate::util::get_field_names(&struct_ast);
    let parameter_list = crate::util::get_parameter_list(&struct_ast);
    let transformed_sql = match crate::numberify::numberify(args.sql, named_parameters) {
        Ok(sql) => sql,
        Err(err) => {
            let err = format!("Error with SQL provided to #[derive(Query)]: {}", err);
            return Err(syn::Error::new(ast.ident.span(), err));
        }
    };

    let generics = ast.generics;
    let ident = ast.ident;
    let where_clause = &generics.where_clause;
    let row_type = args.row;

    let output = quote! {
        #[automatically_derived]
        impl #generics postgres_named_parameters::Query for #ident #generics #where_clause {
            type Row = #row_type;
            fn query_all(
                &self,
                connection: &mut impl postgres_named_parameters::postgres::GenericClient,
            ) -> Result<Vec<Self::Row>, postgres_named_parameters::postgres::error::Error> {
                let rows = connection.query(#transformed_sql, #parameter_list)?;
                rows
                    .iter()
                    .map(postgres_named_parameters::internal::wrapper_for_derive_macro::try_from_row::<Self::Row>)
                    .collect()
            }

            fn query_opt(
                &self,
                connection: &mut impl postgres_named_parameters::postgres::GenericClient,
            ) -> Result<Option<Self::Row>, postgres_named_parameters::postgres::error::Error> {
                let maybe_row = connection.query_opt(#transformed_sql, #parameter_list)?;
                match maybe_row {
                    None => Ok(None),
                    Some(row) => {
                        let decoded_row = postgres_named_parameters::internal::wrapper_for_derive_macro::try_from_row::<Self::Row>(&row)?;
                        Ok(Some(decoded_row))
                    }
                }
            }

            fn query_one(
                &self,
                connection: &mut impl postgres_named_parameters::postgres::GenericClient,
            ) -> Result<Self::Row, postgres_named_parameters::postgres::error::Error> {
                let row = connection.query_one(#transformed_sql, #parameter_list)?;
                postgres_named_parameters::internal::wrapper_for_derive_macro::try_from_row::<Self::Row>(&row)
            }
        }
    };
    Ok(output.into())
}