#![forbid(unsafe_code)]
extern crate proc_macro;
mod codegen;
#[cfg(feature = "sqlite")]
mod codegen_sqlite;
mod connection;
mod dynamic;
mod offline;
mod parse;
mod pg_enum;
mod sort_enum;
mod sql_norm;
mod stmt_name;
mod suggest;
pub(crate) mod types;
#[cfg(feature = "sqlite")]
mod types_sqlite;
mod validate;
#[cfg(feature = "sqlite")]
mod validate_sqlite;
use proc_macro::TokenStream;
#[proc_macro]
pub fn query(input: TokenStream) -> TokenStream {
let input2: proc_macro2::TokenStream = input.into();
match query_impl(input2) {
Ok(output) => output.into(),
Err(err) => err.to_compile_error().into(),
}
}
fn query_impl(input: proc_macro2::TokenStream) -> Result<proc_macro2::TokenStream, syn::Error> {
let sql = extract_sql(input)?;
let parsed = parse::parse_query(&sql)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
#[cfg(feature = "sqlite")]
{
let backend = connection::detect_backend()
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
if backend == Some(connection::Backend::Sqlite) {
return query_impl_sqlite(parsed);
}
}
query_impl_postgres(parsed)
}
fn query_impl_postgres(parsed: parse::ParsedQuery) -> Result<proc_macro2::TokenStream, syn::Error> {
if parsed.sort_placeholder.is_some() {
return query_impl_sort(parsed);
}
let variants = dynamic::expand_variants(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
if parsed.optional_clauses.is_empty() {
let validation = if offline::is_offline() {
offline::lookup_cached_validation(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?
} else {
let result = connection::with_connection(|conn| {
validate::validate_query_with_suggestions(&parsed, conn)
})?;
offline::write_cache(&parsed, &result);
result
};
validate::check_param_types(&parsed, &validation)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
Ok(codegen::generate_query_code(&parsed, &validation))
} else {
let validation = if offline::is_offline() {
offline::lookup_cached_validation(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?
} else {
let result = connection::with_connection(|conn| {
validate::validate_variants(&variants, &parsed, conn)
})?;
offline::write_cache(&parsed, &result);
result
};
Ok(codegen::generate_dynamic_query_code(
&parsed,
&validation,
&variants,
))
}
}
#[cfg(feature = "sqlite")]
fn query_impl_sqlite(parsed: parse::ParsedQuery) -> Result<proc_macro2::TokenStream, syn::Error> {
if parsed.sort_placeholder.is_some() {
return query_impl_sqlite_sort(parsed);
}
let variants = dynamic::expand_variants(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
if parsed.optional_clauses.is_empty() {
let validation = if offline::is_offline() {
offline::lookup_cached_validation(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?
} else {
let result = connection::with_sqlite_connection(|conn| {
validate_sqlite::validate_query_sqlite(&parsed, conn)
})?;
offline::write_cache(&parsed, &result);
result
};
Ok(codegen_sqlite::generate_sqlite_query_code(
&parsed,
&validation,
))
} else {
let validation = if offline::is_offline() {
offline::lookup_cached_validation(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?
} else {
let result = connection::with_sqlite_connection(|conn| {
validate_sqlite::validate_variants_sqlite(&variants, &parsed, conn)
})?;
offline::write_cache(&parsed, &result);
result
};
Ok(codegen_sqlite::generate_dynamic_sqlite_query_code(
&parsed,
&validation,
&variants,
))
}
}
#[cfg(feature = "sqlite")]
fn query_impl_sqlite_sort(
parsed: parse::ParsedQuery,
) -> Result<proc_macro2::TokenStream, syn::Error> {
let sort_placeholder = parsed.sort_placeholder.as_ref().unwrap();
let sort_enum_name = &sort_placeholder.enum_name;
let dummy_sql = parsed.positional_sql.replace("{SORT}", "1");
let dummy_parsed = parse::ParsedQuery {
normalized_sql: parsed.normalized_sql.replace("{sort}", "1"),
positional_sql: dummy_sql,
params: parsed.params.clone(),
kind: parsed.kind,
statement_name: parsed.statement_name.clone(),
optional_clauses: parsed.optional_clauses.clone(),
sort_placeholder: None,
};
let validation = if offline::is_offline() {
offline::lookup_cached_validation(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?
} else {
let result = connection::with_sqlite_connection(|conn| {
validate_sqlite::validate_query_sqlite(&dummy_parsed, conn)
})?;
offline::write_cache(&parsed, &result);
result
};
Ok(codegen_sqlite::generate_sort_sqlite_query_code(
&parsed,
&validation,
sort_enum_name,
))
}
fn query_impl_sort(parsed: parse::ParsedQuery) -> Result<proc_macro2::TokenStream, syn::Error> {
let sort_placeholder = parsed.sort_placeholder.as_ref().unwrap();
let sort_enum_name = &sort_placeholder.enum_name;
let dummy_sql = parsed.positional_sql.replace("{SORT}", "1");
let dummy_parsed = parse::ParsedQuery {
normalized_sql: parsed.normalized_sql.replace("{sort}", "1"),
positional_sql: dummy_sql,
params: parsed.params.clone(),
kind: parsed.kind,
statement_name: parsed.statement_name.clone(),
optional_clauses: parsed.optional_clauses.clone(),
sort_placeholder: None,
};
let validation = if offline::is_offline() {
offline::lookup_cached_validation(&parsed)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?
} else {
let result = connection::with_connection(|conn| {
validate::validate_query_with_suggestions(&dummy_parsed, conn)
})?;
offline::write_cache(&parsed, &result);
result
};
validate::check_param_types(&parsed, &validation)
.map_err(|msg| syn::Error::new(proc_macro2::Span::call_site(), msg))?;
Ok(codegen::generate_sort_query_code(
&parsed,
&validation,
sort_enum_name,
))
}
fn extract_sql(input: proc_macro2::TokenStream) -> Result<String, syn::Error> {
let lit: syn::LitStr = syn::parse2(input)?;
Ok(lit.value())
}
#[proc_macro_attribute]
pub fn pg_enum(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr2: proc_macro2::TokenStream = attr.into();
let item2: proc_macro2::TokenStream = item.into();
match pg_enum::expand_pg_enum(attr2, item2) {
Ok(output) => output.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_attribute]
pub fn sort(attr: TokenStream, item: TokenStream) -> TokenStream {
let attr2: proc_macro2::TokenStream = attr.into();
let item2: proc_macro2::TokenStream = item.into();
match sort_enum::expand_sort_enum(attr2, item2) {
Ok(output) => output.into(),
Err(err) => err.to_compile_error().into(),
}
}