use std::fmt::Display;
use proc_macro2::Span;
use proc_macro2::TokenStream;
use syn::{Ident, Path};
use quote::{format_ident, quote};
use sqlx::{connection::Connection, database::Database};
use super::{args, output, QueryMacroInput};
use crate::database::DatabaseExt;
pub async fn expand_query<C: Connection>(
input: QueryMacroInput,
mut conn: C,
) -> crate::Result<TokenStream>
where
C::Database: DatabaseExt + Sized,
<C::Database as Database>::TypeInfo: Display,
{
let describe = input.describe_validate(&mut conn).await?;
let sql = &input.source;
let args = args::quote_args(&input, &describe, true)?;
let arg_names = &input.arg_names;
let db_path = <C::Database as DatabaseExt>::db_path();
if describe.result_columns.is_empty() {
return Ok(quote! {
macro_rules! macro_result {
(#($#arg_names:expr),*) => {{
use sqlx::arguments::Arguments as _;
#args
sqlx::query::<#db_path>(#sql).bind_all(query_args)
}
}}
});
}
let columns = output::columns_to_rust(&describe)?;
let record_type: Path = Ident::new("Record", Span::call_site()).into();
let record_fields = columns
.iter()
.map(
|&output::RustColumn {
ref ident,
ref type_,
}| quote!(#ident: #type_,),
)
.collect::<TokenStream>();
let query_args = format_ident!("query_args");
let output =
output::quote_query_as::<C::Database>(sql, &record_type, &query_args, &columns, true);
Ok(quote! {
macro_rules! macro_result {
(#($#arg_names:expr),*) => {{
use sqlx::arguments::Arguments as _;
#[derive(Debug)]
struct #record_type {
#record_fields
}
#args
#output
}
}}
})
}