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}