bottle_orm_macro/lib.rs
1//! # Bottle ORM Procedural Macros
2//!
3//! This crate provides procedural macros for the Bottle ORM framework.
4//! It contains the `#[derive(Model)]` macro that automatically implements
5//! the `Model` trait for structs representing database tables.
6//!
7//! ## Overview
8//!
9//! The procedural macros in this crate analyze struct definitions and their
10//! attributes to generate the necessary boilerplate code for ORM functionality.
11//! This includes:
12//!
13//! - Table name resolution
14//! - Column metadata generation
15//! - Type mapping from Rust to SQL
16//! - Serialization methods
17//!
18//! ## Architecture
19//!
20//! The crate is organized into three modules:
21//!
22//! - **`lib.rs`** (this file): Entry point and macro definitions
23//! - **`derive_model.rs`**: Implementation of the Model derive macro
24//! - **`types.rs`**: Type mapping utilities (Rust → SQL)
25//!
26//! ## Usage
27//!
28//! This crate is not meant to be used directly. Instead, it's re-exported
29//! by the main `bottle-orm` crate:
30//!
31//! ```rust,ignore
32//! use bottle_orm::Model;
33//! use uuid::Uuid;
34//!
35//! #[derive(Model)]
36//! struct User {
37//! #[orm(primary_key)]
38//! id: Uuid,
39//! username: String,
40//! }
41//! ```
42//!
43//! ## Supported Attributes
44//!
45//! The `#[orm(...)]` attribute supports the following options:
46//!
47//! ### Primary Key
48//! ```rust,ignore
49//! #[orm(primary_key)]
50//! id: Uuid,
51//! ```
52//! Marks the field as the table's primary key. Generates `PRIMARY KEY` constraint.
53//!
54//! ### Unique Constraint
55//! ```rust,ignore
56//! #[orm(unique)]
57//! username: String,
58//! ```
59//! Adds a `UNIQUE` constraint to prevent duplicate values.
60//!
61//! ### Database Index
62//! ```rust,ignore
63//! #[orm(index)]
64//! email: String,
65//! ```
66//! Creates a database index on the column for faster queries.
67//!
68//! ### Column Size
69//! ```rust,ignore
70//! #[orm(size = 100)]
71//! username: String,
72//! ```
73//! Sets `VARCHAR(N)` size for String fields. Default is `TEXT`.
74//!
75//! ### Auto-Timestamp (Creation)
76//! ```rust,ignore
77//! #[orm(create_time)]
78//! created_at: DateTime<Utc>,
79//! ```
80//! Adds `DEFAULT CURRENT_TIMESTAMP` to auto-populate on INSERT.
81//!
82//! ### Auto-Timestamp (Update)
83//! ```rust,ignore
84//! #[orm(update_time)]
85//! updated_at: DateTime<Utc>,
86//! ```
87//! Auto-updates timestamp on UPDATE (future feature).
88//!
89//! ### Foreign Key
90//! ```rust,ignore
91//! #[orm(foreign_key = "User::id")]
92//! user_id: Uuid,
93//! ```
94//! Creates a foreign key relationship. Format: `"TargetTable::target_column"`.
95//!
96//! ### Omit Field
97//! ```rust,ignore
98//! #[orm(omit)]
99//! password: String,
100//! ```
101//! Excludes this field from query results by default. Returns a placeholder value
102//! instead of the actual data (`"omited"` for strings, `1970-01-01T00:00:00Z` for dates, etc.).
103//!
104//! ### Combining Attributes
105//! ```rust,ignore
106//! #[orm(size = 50, unique, index)]
107//! username: String,
108//! ```
109//! Multiple attributes can be combined on a single field.
110//!
111//! ## Generated Field Constants
112//!
113//! The macro also generates a `{model}_fields` module with constants for each field,
114//! enabling IDE autocomplete:
115//!
116//! ```rust,ignore
117//! // For struct User, the macro generates:
118//! pub mod user_fields {
119//! pub const ID: &'static str = "id";
120//! pub const USERNAME: &'static str = "username";
121//! pub const PASSWORD: &'static str = "password";
122//! }
123//!
124//! // Use with filter, select, omit, etc:
125//! db.model::<User>()
126//! .filter(user_fields::AGE, ">=", 18)
127//! .omit(user_fields::PASSWORD)
128//! .scan()
129//! .await?;
130//! ```
131//!
132//! ## Type Support
133//!
134//! The macro supports automatic type mapping for:
135//!
136//! ### Primitive Types
137//! - `i32` → `INTEGER`
138//! - `i64` → `BIGINT`
139//! - `f64` → `DOUBLE PRECISION`
140//! - `bool` → `BOOLEAN`
141//! - `String` → `TEXT` or `VARCHAR(N)`
142//!
143//! ### UUID Types (All Versions 1-7)
144//! - `Uuid` → `UUID`
145//!
146//! ### Date/Time Types
147//! - `DateTime<Utc>` → `TIMESTAMPTZ`
148//! - `NaiveDateTime` → `TIMESTAMP`
149//! - `NaiveDate` → `DATE`
150//! - `NaiveTime` → `TIME`
151//!
152//! ### Nullable Types
153//! - `Option<T>` → SQL type of `T` with `NULL` allowed
154//!
155//! ## Complete Example
156//!
157//! ```rust,ignore
158//! use bottle_orm::Model;
159//! use uuid::Uuid;
160//! use chrono::{DateTime, Utc};
161//! use serde::{Deserialize, Serialize};
162//! use sqlx::FromRow;
163//!
164//! #[derive(Model, Debug, Clone, Serialize, Deserialize, FromRow)]
165//! struct User {
166//! #[orm(primary_key)]
167//! id: Uuid,
168//!
169//! #[orm(size = 50, unique, index)]
170//! username: String,
171//!
172//! #[orm(size = 100, unique)]
173//! email: String,
174//!
175//! age: Option<i32>,
176//!
177//! active: bool,
178//!
179//! #[orm(create_time)]
180//! created_at: DateTime<Utc>,
181//!
182//! #[orm(update_time)]
183//! updated_at: Option<DateTime<Utc>>,
184//! }
185//!
186//! #[derive(Model, Debug, Clone, Serialize, Deserialize, FromRow)]
187//! struct Post {
188//! #[orm(primary_key)]
189//! id: Uuid,
190//!
191//! #[orm(foreign_key = "User::id", index)]
192//! user_id: Uuid,
193//!
194//! #[orm(size = 200)]
195//! title: String,
196//!
197//! content: String,
198//!
199//! published: bool,
200//!
201//! #[orm(create_time)]
202//! created_at: DateTime<Utc>,
203//! }
204//! ```
205
206// ============================================================================
207// Compiler Directives
208// ============================================================================
209
210// Ensure this crate is only used as a proc-macro crate
211#![warn(missing_docs)]
212
213// ============================================================================
214// External Crate Imports
215// ============================================================================
216
217use proc_macro::TokenStream;
218use syn::{parse_macro_input, DeriveInput};
219
220// ============================================================================
221// Module Declarations
222// ============================================================================
223
224/// Type mapping module - converts Rust types to SQL types.
225///
226/// This module contains the logic for mapping Rust types (including primitives,
227/// UUID, chrono types, and Option<T>) to their corresponding SQL type strings.
228mod types;
229
230/// Model derive implementation module.
231///
232/// This module contains the core logic for expanding the `#[derive(Model)]`
233/// macro, including attribute parsing and code generation.
234mod derive_model;
235
236/// Enum derive implementation module.
237mod derive_enum;
238
239/// FromAnyRow derive implementation module.
240mod derive_anyrow;
241
242// ============================================================================
243// Procedural Macro Definitions
244// ============================================================================
245
246/// Derives the `Model` trait for a struct.
247///
248/// This procedural macro inspects the struct fields and generates the necessary
249/// code to map the struct to a database table. It automatically implements the
250/// `Model` trait with methods for retrieving table metadata and converting
251/// instances to/from database format.
252///
253/// # Supported Attributes
254///
255/// The macro recognizes the following `#[orm(...)]` attributes on struct fields:
256///
257/// * `primary_key` - Marks the field as a primary key
258/// * `unique` - Adds a UNIQUE constraint
259/// * `index` - Creates a database index
260/// * `create_time` - Sets default value to CURRENT_TIMESTAMP
261/// * `update_time` - Auto-updates timestamp (future feature)
262/// * `size = N` - Sets column size (VARCHAR(N))
263/// * `foreign_key = "Table::Column"` - Defines a Foreign Key relationship
264/// * `omit` - Excludes field from queries (returns placeholder value)
265///
266/// # Type Mapping
267///
268/// The macro automatically maps Rust types to SQL types:
269///
270/// - **Primitives**: `i32` → INTEGER, `i64` → BIGINT, `bool` → BOOLEAN, etc.
271/// - **UUID**: `Uuid` → UUID (supports all versions 1-7)
272/// - **Strings**: `String` → TEXT or VARCHAR(N) with size attribute
273/// - **Date/Time**: `DateTime<Utc>` → TIMESTAMPTZ, etc.
274/// - **Nullable**: `Option<T>` → SQL type of T with NULL allowed
275///
276/// # Requirements
277///
278/// The struct must have named fields. Tuple structs and unit structs are not supported.
279///
280/// # Generated Implementation
281///
282/// The macro generates an implementation of the `Model` trait with four methods:
283///
284/// 1. `table_name()` - Returns the struct name as a static string
285/// 2. `columns()` - Returns column metadata as `Vec<ColumnInfo>`
286/// 3. `active_columns()` - Returns column names as `Vec<&'static str>`
287/// 4. `to_map()` - Serializes the instance to `HashMap<String, String>`
288///
289/// # Example
290///
291/// ```rust,ignore
292/// use bottle_orm::Model;
293/// use uuid::Uuid;
294/// use chrono::{DateTime, Utc};
295///
296/// #[derive(Model)]
297/// struct User {
298/// #[orm(primary_key)]
299/// id: Uuid,
300///
301/// #[orm(size = 50, unique)]
302/// username: String,
303///
304/// #[orm(size = 100)]
305/// email: String,
306///
307/// age: i32,
308///
309/// #[orm(create_time)]
310/// created_at: DateTime<Utc>,
311/// }
312/// ```
313///
314/// # Panics
315///
316/// The macro will panic at compile time if:
317///
318/// - The input is not a struct
319/// - The struct doesn't have named fields
320/// - An `#[orm(...)]` attribute is malformed
321/// - A `foreign_key` attribute doesn't follow the "Table::Column" format
322///
323/// # See Also
324///
325/// * [`Model`](../bottle_orm/trait.Model.html) - The trait being implemented
326/// * [`ColumnInfo`](../bottle_orm/struct.ColumnInfo.html) - Column metadata structure
327#[proc_macro_derive(Model, attributes(orm))]
328pub fn model_derive(input: TokenStream) -> TokenStream {
329 // Parse the input tokens into a syntax tree
330 let ast = parse_macro_input!(input as DeriveInput);
331
332 // Expand the macro to generate the Model trait implementation
333 let expanded = derive_model::expand(ast);
334
335 // Convert the generated code back into a TokenStream
336 TokenStream::from(expanded)
337}
338
339/// Derives `Display` and `FromStr` for an enum to facilitate database mapping.
340///
341/// This macro generates implementations that allow the enum to be easily saved
342/// as a string (via `to_string()`) and loaded back from a string (via `parse()`).
343/// It uses the variant names as the string representation.
344///
345/// # Example
346///
347/// ```rust,ignore
348/// #[derive(BottleEnum, Debug, Clone, PartialEq)]
349/// enum UserStatus {
350/// Active,
351/// Inactive,
352/// Pending,
353/// }
354/// ```
355#[proc_macro_derive(BottleEnum)]
356pub fn enum_derive(input: TokenStream) -> TokenStream {
357 let ast = parse_macro_input!(input as DeriveInput);
358 let expanded = derive_enum::expand(ast);
359 TokenStream::from(expanded)
360}
361
362/// Derives the `FromRow` trait for `AnyRow` and the `AnyImpl` trait.
363///
364/// This procedural macro generates an implementation of `sqlx::FromRow<'r, sqlx::any::AnyRow>`
365/// for the target struct, allowing it to be scanned directly from database results when
366/// using `sqlx::Any` driver (which Bottle ORM uses internally).
367///
368/// It also implements the `AnyImpl` trait, which provides necessary column metadata used
369/// by the `QueryBuilder` for dynamic query construction.
370///
371/// # Features
372///
373/// - **Automatic Field Mapping**: Maps database columns to struct fields by name.
374/// - **DateTime Handling**: Includes special logic to handle `DateTime` types, often required
375/// when dealing with the `Any` driver's type erasure or JSON serialization fallback.
376/// - **Metadata Generation**: Automatically generates `AnyInfo` for each field.
377///
378/// # Requirements
379///
380/// The struct must have named fields. Tuple structs and unit structs are not supported.
381///
382/// # Example
383///
384/// ```rust,ignore
385/// use bottle_orm::{FromAnyRow, AnyImpl};
386/// use chrono::{DateTime, Utc};
387///
388/// #[derive(FromAnyRow)]
389/// struct UserCount {
390/// count: i64,
391/// last_active: DateTime<Utc>,
392/// }
393///
394/// // Usage with QueryBuilder:
395/// // let stats: UserCount = db.model::<User>().select("count(*), last_active").first().await?;
396/// ```
397#[proc_macro_derive(FromAnyRow)]
398pub fn any_derive(input: TokenStream) -> TokenStream {
399 let ast = parse_macro_input!(input as DeriveInput);
400 let expanded = derive_anyrow::expand(ast);
401 TokenStream::from(expanded)
402}