Skip to main content

sqlx_conditional_queries_macros/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use proc_macro_error2::abort;
4use sqlx_conditional_queries_core::{AnalyzeError, DatabaseType, Error, ExpandError};
5
6const DATABASE_TYPE: DatabaseType = if cfg!(feature = "postgres") {
7    DatabaseType::PostgreSql
8} else if cfg!(feature = "mysql") {
9    DatabaseType::MySql
10} else if cfg!(feature = "sqlite") {
11    DatabaseType::Sqlite
12} else {
13    panic!("No database feature was enabled")
14};
15
16// The public docs for this macro live in the sql-conditional-queries crate.
17#[proc_macro_error2::proc_macro_error]
18#[proc_macro]
19pub fn conditional_query_as(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
20    conditional_query_as_inner(input, true)
21}
22
23// The public docs for this macro live in the sql-conditional-queries crate.
24#[proc_macro_error2::proc_macro_error]
25#[proc_macro]
26pub fn conditional_query_as_unchecked(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
27    conditional_query_as_inner(input, false)
28}
29
30fn conditional_query_as_inner(
31    input: proc_macro::TokenStream,
32    checked: bool,
33) -> proc_macro::TokenStream {
34    let input: proc_macro2::TokenStream = input.into();
35
36    let ts = match sqlx_conditional_queries_core::conditional_query_as(
37        DATABASE_TYPE,
38        input,
39        checked,
40    ) {
41        Ok(ts) => ts,
42        Err(Error::SynError(err)) => {
43            return err.to_compile_error().into();
44        }
45        Err(Error::AnalyzeError(err)) => match err {
46            AnalyzeError::ExpectedStringLiteral(span) => abort!(
47                span,
48                "expected string literal";
49                help = "only string literals or tuples of string literals are supported in compile-time bindings";
50            ),
51            AnalyzeError::BindingNameValueLengthMismatch {
52                names,
53                names_span,
54                values,
55                values_span,
56            } => abort!(
57                names_span,
58                "mismatch between number of names and values";
59                names = names_span => "number of names: {}", names;
60                values = values_span => "number of values: {}", values;
61            ),
62            AnalyzeError::DuplicatedCompileTimeBindingsFound { first: _, second } => {
63                abort!(second.span(), "found duplicate compile-time binding")
64            }
65            AnalyzeError::CompileTimeBindingCycleDetected { root_ident, path } => abort!(
66                root_ident.span(),
67                "detected compile-time binding cycle: {}",
68                path
69            ),
70        },
71        Err(Error::ExpandError(err)) => match err {
72            // TODO: Make this span point at the binding reference.  Requires https://github.com/rust-lang/rust/issues/54725
73            ExpandError::MissingCompileTimeBinding(binding, span) => abort!(
74                span,
75                "missing compile-time binding";
76                help = "found no compile-time binding with the specified name: {}", binding;
77            ),
78            // TODO: Make this span point at the opening brace.  Requires https://github.com/rust-lang/rust/issues/54725
79            ExpandError::MissingBindingClosingBrace(span) => abort!(
80                span,
81                "missing closing brace for compile-time binding reference"
82            ),
83            ExpandError::BindingReferenceTypeOverrideParseError(err, span) => abort!(
84                span,
85                "failed to parse type override in binding reference: {}",
86                err
87            ),
88        },
89    };
90
91    let output: proc_macro::TokenStream = ts.into();
92    output
93}