postgres_named_parameters_derive/
query.rs

1use attribute_derive::FromAttr;
2use quote::quote;
3use syn::{DeriveInput, Type};
4
5#[derive(FromAttr)]
6#[attribute(ident = query)]
7struct QueryTraitHelperAttribute {
8    #[attribute(example = r#""SELECT * FROM Person WHERE first_name = @name""#)]
9    sql: String,
10    #[attribute(example = "crate::my_database_tables::Person")]
11    row: Type,
12}
13
14pub fn derive_query_impl(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream> {
15    let args = QueryTraitHelperAttribute::from_attributes(&ast.attrs)?;
16    let syn::Data::Struct(struct_ast) = ast.data else {
17        return Err(syn::Error::new(
18            ast.ident.span(),
19            "#[derive(Query)] can only be used on structs",
20        ));
21    };
22    let named_parameters = crate::util::get_field_names(&struct_ast);
23    let parameter_list = crate::util::get_parameter_list(&struct_ast);
24    let transformed_sql = match crate::numberify::numberify(args.sql, named_parameters) {
25        Ok(sql) => sql,
26        Err(err) => {
27            let err = format!("Error with SQL provided to #[derive(Query)]: {}", err);
28            return Err(syn::Error::new(ast.ident.span(), err));
29        }
30    };
31
32    let generics = ast.generics;
33    let ident = ast.ident;
34    let where_clause = &generics.where_clause;
35    let row_type = args.row;
36
37    let output = quote! {
38        #[automatically_derived]
39        impl #generics postgres_named_parameters::Query for #ident #generics #where_clause {
40            type Row = #row_type;
41            fn query_all(
42                &self,
43                connection: &mut impl postgres_named_parameters::postgres::GenericClient,
44            ) -> Result<Vec<Self::Row>, postgres_named_parameters::postgres::error::Error> {
45                let rows = connection.query(#transformed_sql, #parameter_list)?;
46                rows
47                    .iter()
48                    .map(postgres_named_parameters::internal::wrapper_for_derive_macro::try_from_row::<Self::Row>)
49                    .collect()
50            }
51
52            fn query_opt(
53                &self,
54                connection: &mut impl postgres_named_parameters::postgres::GenericClient,
55            ) -> Result<Option<Self::Row>, postgres_named_parameters::postgres::error::Error> {
56                let maybe_row = connection.query_opt(#transformed_sql, #parameter_list)?;
57                match maybe_row {
58                    None => Ok(None),
59                    Some(row) => {
60                        let decoded_row = postgres_named_parameters::internal::wrapper_for_derive_macro::try_from_row::<Self::Row>(&row)?;
61                        Ok(Some(decoded_row))
62                    }
63                }
64            }
65
66            fn query_one(
67                &self,
68                connection: &mut impl postgres_named_parameters::postgres::GenericClient,
69            ) -> Result<Self::Row, postgres_named_parameters::postgres::error::Error> {
70                let row = connection.query_one(#transformed_sql, #parameter_list)?;
71                postgres_named_parameters::internal::wrapper_for_derive_macro::try_from_row::<Self::Row>(&row)
72            }
73        }
74    };
75    Ok(output.into())
76}