sqlw_macro 0.1.0

Procedural macros for sqlw
Documentation
//! Procedural macros for sqlw.
//!
//! This crate provides the macros used by [`sqlw`]. You typically
//! don't need to depend on it directly; use the re-exports from `sqlw`.
//!
//! [`sqlw`]: https://docs.rs/sqlw

use proc_macro::TokenStream;
use syn::parse_macro_input;

use crate::{
    query::{
        codegen::{CodeGenerator, PlaceholderStyle},
        parser::ParsedQuery,
    },
    row::{codegen::FromRowGenerator, into_value, parser::FromRowInput, try_from_value_ref},
};

mod query;
mod row;
mod schema;

/// Compiles a SQL query with `?` placeholders.
///
/// # Syntax
///
/// - `Schema::FIELD` - Field references resolved via `.desc()`
/// - `{expr}` - Variable binding (emits `?` placeholder)
///
/// # Example
///
/// ```ignore
/// use sqlw::query_qmark;
///
/// let name = "Alice";
/// let query = query_qmark!(
///     SELECT User::NAME, User::EMAIL
///     FROM User::TABLE
///     WHERE User::NAME = {name}
/// );
/// // Result: "SELECT name, email FROM users WHERE name = ?"
/// ```
#[proc_macro]
pub fn query_qmark(input: TokenStream) -> TokenStream {
    let parsed = parse_macro_input!(input as ParsedQuery);
    let generator = CodeGenerator::new(parsed, PlaceholderStyle::Qmark);
    generator.generate().into()
}

/// Compiles a SQL query with `$1, $2` placeholders.
///
/// # Syntax
///
/// - `Schema::FIELD` - Field references resolved via `.desc()`
/// - `{expr}` - Variable binding (emits `$1, $2` placeholders)
///
/// # Example
///
/// ```ignore
/// use sqlw::query_numbered;
///
/// let name = "Alice";
/// let query = query_numbered!(
///     SELECT User::NAME, User::EMAIL
///     FROM User::TABLE
///     WHERE User::NAME = {name}
/// );
/// // Result: "SELECT name, email FROM users WHERE name = $1"
/// ```
#[proc_macro]
pub fn query_numbered(input: TokenStream) -> TokenStream {
    let parsed = parse_macro_input!(input as ParsedQuery);
    let generator = CodeGenerator::new(parsed, PlaceholderStyle::Numbered);
    generator.generate().into()
}

/// Derives [`FromRow`] for a struct.
///
/// # Attributes
///
/// - `#[field = "column_name"]` - Map to a specific column name
/// - `#[field(Schema::FIELD)]` - Map to a schema field constant
/// - `#[optional]` - Allow the column to be missing (returns `None`)
///
/// # Example
///
/// ```ignore
/// use sqlw::FromRow;
///
/// #[derive(FromRow)]
/// struct User {
///     id: i64,
///     #[field = "user_name"]
///     name: String,
///     #[optional]
///     bio: Option<String>,
/// }
/// ```
#[proc_macro_derive(FromRow, attributes(column, field, optional))]
pub fn derive_from_row(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as FromRowInput);
    let generator = FromRowGenerator::new(input);
    generator.generate().into()
}

/// Derives [`TryFrom<ValueRef>`] for a single-field tuple struct (newtype).
///
/// Delegates to the inner type's [`TryFrom<ValueRef>`] implementation.
///
/// # Example
///
/// ```ignore
/// use sqlw::TryFromValueRef;
///
/// #[derive(TryFromValueRef)]
/// struct UserName(String);
/// ```
#[proc_macro_derive(TryFromValueRef)]
pub fn derive_try_from_value_ref(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    try_from_value_ref::generate(input).into()
}

/// Derives [`From<T> for Value`] for newtypes and enums.
///
/// For newtypes, delegates to the inner type's [`Into<Value>`].
/// For enums, maps variants to strings via `#[value = "..."]` (defaults to lowercased name).
///
/// # Examples
///
/// ```ignore
/// use sqlw::IntoValue;
///
/// #[derive(IntoValue)]
/// struct UserName(String);
///
/// #[derive(IntoValue)]
/// enum Role {
///     #[value = "user"]
///     User,
///     Admin,
/// }
/// ```
#[proc_macro_derive(IntoValue, attributes(value))]
pub fn derive_into_value(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as syn::DeriveInput);
    into_value::generate(input).into()
}

/// Defines a table schema with typed field constants and a value struct.
///
/// Generates:
/// - A struct with one `pub` field per column (named from the column string)
/// - `TABLE` and field constants for compile-time-safe SQL references
/// - A `Default` impl so you can create zero-value instances
///
/// Each row in the macro body is: `NAME: Type "column_name"`.
/// The UPPER_CASE `NAME` becomes a `const` for SQL generation;
/// the `column_name` string becomes the struct field name.
///
/// # Example
///
/// ```ignore
/// schema!(User "users" {
///     /// Primary key
///     ID: i64 "id",
///     NAME: String "name",
///     EMAIL: String "email",
/// });
///
/// // Generated value struct:
/// // pub struct User {
/// //     pub id: i64,
/// //     pub name: String,
/// //     pub email: String,
/// // }
/// // impl Default for User { ... }
///
/// // Generated constants:
/// // User::TABLE -> Def<User>
/// // User::ID   -> Def<User, Typed<i64>>
/// // User::NAME -> Def<User, Typed<String>>
/// // User::EMAIL -> Def<User, Typed<String>>
/// ```
///
/// # Untyped fields
///
/// When you only need a column constant without a corresponding struct field
/// (e.g. for computed columns or foreign key references), omit the type:
///
/// ```ignore
/// schema!(User "users" {
///     ID "id",             // const only, no struct field
///     NAME: String "name", // const and struct field
/// });
/// ```
#[proc_macro]
pub fn schema(input: TokenStream) -> TokenStream {
    let parsed = parse_macro_input!(input as schema::parser::SchemaInput);
    let generator = schema::codegen::SchemaGenerator::new(parsed);
    generator.generate().into()
}