Skip to main content

sqlw_macro/
lib.rs

1//! Procedural macros for sqlw.
2//!
3//! This crate provides the macros used by [`sqlw`]. You typically
4//! don't need to depend on it directly; use the re-exports from `sqlw`.
5//!
6//! [`sqlw`]: https://docs.rs/sqlw
7
8use proc_macro::TokenStream;
9use syn::parse_macro_input;
10
11use crate::{
12    query::{
13        codegen::{CodeGenerator, PlaceholderStyle},
14        parser::ParsedQuery,
15    },
16    row::{codegen::FromRowGenerator, into_value, parser::FromRowInput, try_from_value_ref},
17};
18
19mod query;
20mod row;
21mod schema;
22
23/// Compiles a SQL query with `?` placeholders.
24///
25/// # Syntax
26///
27/// - `Schema::FIELD` - Field references resolved via `.desc()`
28/// - `{expr}` - Variable binding (emits `?` placeholder)
29///
30/// # Example
31///
32/// ```ignore
33/// use sqlw::query_qmark;
34///
35/// let name = "Alice";
36/// let query = query_qmark!(
37///     SELECT User::NAME, User::EMAIL
38///     FROM User::TABLE
39///     WHERE User::NAME = {name}
40/// );
41/// // Result: "SELECT name, email FROM users WHERE name = ?"
42/// ```
43#[proc_macro]
44pub fn query_qmark(input: TokenStream) -> TokenStream {
45    let parsed = parse_macro_input!(input as ParsedQuery);
46    let generator = CodeGenerator::new(parsed, PlaceholderStyle::Qmark);
47    generator.generate().into()
48}
49
50/// Compiles a SQL query with `$1, $2` placeholders.
51///
52/// # Syntax
53///
54/// - `Schema::FIELD` - Field references resolved via `.desc()`
55/// - `{expr}` - Variable binding (emits `$1, $2` placeholders)
56///
57/// # Example
58///
59/// ```ignore
60/// use sqlw::query_numbered;
61///
62/// let name = "Alice";
63/// let query = query_numbered!(
64///     SELECT User::NAME, User::EMAIL
65///     FROM User::TABLE
66///     WHERE User::NAME = {name}
67/// );
68/// // Result: "SELECT name, email FROM users WHERE name = $1"
69/// ```
70#[proc_macro]
71pub fn query_numbered(input: TokenStream) -> TokenStream {
72    let parsed = parse_macro_input!(input as ParsedQuery);
73    let generator = CodeGenerator::new(parsed, PlaceholderStyle::Numbered);
74    generator.generate().into()
75}
76
77/// Derives [`FromRow`] for a struct.
78///
79/// # Attributes
80///
81/// - `#[field = "column_name"]` - Map to a specific column name
82/// - `#[field(Schema::FIELD)]` - Map to a schema field constant
83/// - `#[optional]` - Allow the column to be missing (returns `None`)
84///
85/// # Example
86///
87/// ```ignore
88/// use sqlw::FromRow;
89///
90/// #[derive(FromRow)]
91/// struct User {
92///     id: i64,
93///     #[field = "user_name"]
94///     name: String,
95///     #[optional]
96///     bio: Option<String>,
97/// }
98/// ```
99#[proc_macro_derive(FromRow, attributes(column, field, optional))]
100pub fn derive_from_row(input: TokenStream) -> TokenStream {
101    let input = parse_macro_input!(input as FromRowInput);
102    let generator = FromRowGenerator::new(input);
103    generator.generate().into()
104}
105
106/// Derives [`TryFrom<ValueRef>`] for a single-field tuple struct (newtype).
107///
108/// Delegates to the inner type's [`TryFrom<ValueRef>`] implementation.
109///
110/// # Example
111///
112/// ```ignore
113/// use sqlw::TryFromValueRef;
114///
115/// #[derive(TryFromValueRef)]
116/// struct UserName(String);
117/// ```
118#[proc_macro_derive(TryFromValueRef)]
119pub fn derive_try_from_value_ref(input: TokenStream) -> TokenStream {
120    let input = parse_macro_input!(input as syn::DeriveInput);
121    try_from_value_ref::generate(input).into()
122}
123
124/// Derives [`From<T> for Value`] for newtypes and enums.
125///
126/// For newtypes, delegates to the inner type's [`Into<Value>`].
127/// For enums, maps variants to strings via `#[value = "..."]` (defaults to lowercased name).
128///
129/// # Examples
130///
131/// ```ignore
132/// use sqlw::IntoValue;
133///
134/// #[derive(IntoValue)]
135/// struct UserName(String);
136///
137/// #[derive(IntoValue)]
138/// enum Role {
139///     #[value = "user"]
140///     User,
141///     Admin,
142/// }
143/// ```
144#[proc_macro_derive(IntoValue, attributes(value))]
145pub fn derive_into_value(input: TokenStream) -> TokenStream {
146    let input = parse_macro_input!(input as syn::DeriveInput);
147    into_value::generate(input).into()
148}
149
150/// Defines a table schema with typed field constants and a value struct.
151///
152/// Generates:
153/// - A struct with one `pub` field per column (named from the column string)
154/// - `TABLE` and field constants for compile-time-safe SQL references
155/// - A `Default` impl so you can create zero-value instances
156///
157/// Each row in the macro body is: `NAME: Type "column_name"`.
158/// The UPPER_CASE `NAME` becomes a `const` for SQL generation;
159/// the `column_name` string becomes the struct field name.
160///
161/// # Example
162///
163/// ```ignore
164/// schema!(User "users" {
165///     /// Primary key
166///     ID: i64 "id",
167///     NAME: String "name",
168///     EMAIL: String "email",
169/// });
170///
171/// // Generated value struct:
172/// // pub struct User {
173/// //     pub id: i64,
174/// //     pub name: String,
175/// //     pub email: String,
176/// // }
177/// // impl Default for User { ... }
178///
179/// // Generated constants:
180/// // User::TABLE -> Def<User>
181/// // User::ID   -> Def<User, Typed<i64>>
182/// // User::NAME -> Def<User, Typed<String>>
183/// // User::EMAIL -> Def<User, Typed<String>>
184/// ```
185///
186/// # Untyped fields
187///
188/// When you only need a column constant without a corresponding struct field
189/// (e.g. for computed columns or foreign key references), omit the type:
190///
191/// ```ignore
192/// schema!(User "users" {
193///     ID "id",             // const only, no struct field
194///     NAME: String "name", // const and struct field
195/// });
196/// ```
197#[proc_macro]
198pub fn schema(input: TokenStream) -> TokenStream {
199    let parsed = parse_macro_input!(input as schema::parser::SchemaInput);
200    let generator = schema::codegen::SchemaGenerator::new(parsed);
201    generator.generate().into()
202}