use {
crate::parser::{self, QueryType},
proc_macro2::TokenStream,
syn::{Generics, Ident},
};
pub fn new_impl(
ident: &Ident,
generics: Generics,
sql: Option<String>,
postgres: Option<String>,
sqlite: Option<String>,
) -> TokenStream {
if sql.is_none() && postgres.is_none() {
panic!("you must provide an sql or postgres query")
}
if sql.is_none() && sqlite.is_none() {
panic!("you must provide an sql or sqlite query")
}
let (postgres_sql, postgres_params) = if cfg!(feature = "postgresql") {
let p = sql_params(QueryType::PostgreSQL, postgres, sql.clone());
(Some(p.0), Some(p.1))
} else {
(None, None)
};
let (sqlite_sql, sqlite_params) = if cfg!(feature = "sqlite") {
let p = sql_params(QueryType::SQLite, sqlite, sql);
(Some(p.0), Some(p.1))
} else {
(None, None)
};
if cfg!(all(feature = "postgresql", feature = "sqlite"))
&& postgres_params.clone().unwrap().len() != sqlite_params.clone().unwrap().len()
{
panic!("the queries for postgres and sqlite do not have a matching amount of parameters");
}
let param_count = if cfg!(feature = "postgresql") {
postgres_params.clone().unwrap().len()
} else if cfg!(feature = "sqlite") {
sqlite_params.clone().unwrap().len()
} else {
0
};
let mut new_generics = generics.clone();
new_generics.params.push(syn::parse_quote!('__query));
let (_, type_generics, _) = generics.split_for_impl();
let (impl_generics, _, where_clause) = new_generics.split_for_impl();
let postgresql = if cfg!(feature = "postgresql") {
let sql = postgres_sql.unwrap();
let params = postgres_params.unwrap();
Some(quote::quote! {
fn sql_postgres(&'__query self) -> Self::Sql {
#sql
}
type ParamsPostgres = [&'__query (dyn ::db_derive::internal::PostgresToSQL + Sync); #param_count];
fn params_postgres(&'__query self) -> Self::ParamsPostgres {
[ #( &self.#params, )* ]
}
})
} else {
None
};
let sqlite: Option<TokenStream> = if cfg!(feature = "sqlite") {
let sql = sqlite_sql.unwrap();
let params = sqlite_params.unwrap();
Some(quote::quote! {
fn sql_sqlite(&'__query self) -> Self::Sql {
#sql
}
type ParamsSQLite = [&'__query dyn ::db_derive::internal::SQLiteToSQL; #param_count];
fn params_sqlite(&'__query self) -> Self::ParamsSQLite {
[ #( &self.#params, )* ]
}
})
} else {
None
};
quote::quote! {
impl #impl_generics ::db_derive::prelude::Sql<'__query> for #ident #type_generics #where_clause {
type Sql = &'__query str;
#postgresql
#sqlite
}
}
}
pub fn sql_params(
typ: QueryType,
specific: Option<String>,
fallback: Option<String>,
) -> (String, Vec<Ident>) {
let query = if let Some(sql) = specific {
sql
} else {
fallback.expect("you must provide sql, postgres, or sqlite queries")
};
let (sql, params) = parser::parse_query(typ, query);
let mut sorted_params = params.into_iter().collect::<Vec<_>>();
sorted_params.sort_by_key(|(_, index)| *index);
(
sql,
sorted_params
.into_iter()
.map(|(ident, _)| ident)
.collect::<Vec<_>>(),
)
}