chaindexing_macros/
lib.rs

1//! # Chaindexing Macros
2//! 
3//! This crate provides procedural macros for the Chaindexing library.
4//! 
5//! **Note**: These macros are re-exported by the main `chaindexing` crate.
6//! Users should import them from `chaindexing` instead of depending on this crate directly:
7//! 
8//! ```rust
9//! use chaindexing::state_migrations;
10//! // or
11//! use chaindexing::prelude::*;
12//! ```
13
14use proc_macro::TokenStream;
15use quote::quote;
16use syn::{parse_macro_input, Expr, ExprArray, Lit};
17
18/// Validates SQL migration strings at compile time and re-emits them as a
19/// `&[&'static str]` slice literal.
20///
21/// # Example
22/// ```
23/// use chaindexing_macros::state_migrations;
24/// const MIGRATIONS: &[&str] = state_migrations!([
25///     r#"CREATE TABLE IF NOT EXISTS foo (id BIGSERIAL PRIMARY KEY)"#,
26///     r#"CREATE INDEX IF NOT EXISTS idx_id ON foo(id)"#
27/// ]);
28/// ```
29#[proc_macro]
30pub fn state_migrations(input: TokenStream) -> TokenStream {
31    // Expect an array literal like ["SQL1", "SQL2", ...]
32    let ExprArray { elems, .. } = parse_macro_input!(input as ExprArray);
33
34    let dialect = sqlparser::dialect::PostgreSqlDialect {};
35
36    for expr in &elems {
37        let lit_str = match expr {
38            Expr::Lit(expr_lit) => match &expr_lit.lit {
39                Lit::Str(s) => s,
40                _ => {
41                    return syn::Error::new_spanned(
42                        expr,
43                        "state_migrations! expects string literals",
44                    )
45                    .to_compile_error()
46                    .into();
47                }
48            },
49            _ => {
50                return syn::Error::new_spanned(expr, "state_migrations! expects string literals")
51                    .to_compile_error()
52                    .into();
53            }
54        };
55
56        if let Err(e) = sqlparser::parser::Parser::parse_sql(&dialect, &lit_str.value()) {
57            let msg = format!("SQL parse error: {e}");
58            return quote! { compile_error!(#msg); }.into();
59        }
60    }
61
62    // All good – turn the string literals into a slice reference.
63    let elems_iter = elems.iter();
64    quote! {
65        &[ #( #elems_iter ),* ]
66    }
67    .into()
68}