docs: rewrite documentation for core traits and library

Completely rewrite and expand documentation for Georm’s core
functionality with detailed explanations, examples, and implementation
details.

Changes:
- Rewrite lib.rs with comprehensive library documentation covering all
  features
- Add extensive Georm trait documentation with method-specific details
- Add detailed Defaultable trait documentation with usage patterns

The documentation now provides complete coverage of:
- All CRUD operations with database behavior details
- Composite key support and generated ID structs
- Defaultable field patterns and companion struct generation
- Relationship modeling (field-level and struct-level)
- Error handling and performance characteristics
- PostgreSQL-specific features and optimizations
This commit is contained in:
Lucien Cartier-Tilet 2025-06-09 22:16:38 +02:00
parent 19284665e6
commit a7696270da
Signed by: phundrak
SSH Key Fingerprint: SHA256:CE0HPsbW3L2YiJETx1zYZ2muMptaAqTN2g3498KrMkc
3 changed files with 1043 additions and 309 deletions

View File

@ -1,10 +1,278 @@
/// Trait for creating entities with database defaults and auto-generated values.
///
/// This trait is automatically implemented on generated companion structs for entities
/// that have fields marked with `#[georm(defaultable)]`. It provides a convenient way
/// to create entities while allowing the database to provide default values for certain
/// fields.
///
/// ## Generated Implementation
///
/// When you mark fields with `#[georm(defaultable)]`, Georm automatically generates:
/// - A companion struct named `{EntityName}Default`
/// - An implementation of this trait for the companion struct
/// - Optimized SQL that omits defaultable fields when they are `None`
///
/// ## How It Works
///
/// The generated companion struct transforms defaultable fields into `Option<T>` types:
/// - `Some(value)` - Use the provided value
/// - `None` - Let the database provide the default value
///
/// Non-defaultable fields remain unchanged and are always required.
///
/// ## Database Behavior
///
/// The `create` method generates SQL that:
/// - Only includes fields where `Some(value)` is provided
/// - Omits fields that are `None`, allowing database defaults to apply
/// - Uses `RETURNING *` to capture the final entity state with all defaults applied
/// - Respects database triggers, sequences, and default value expressions
///
/// ## Usage Examples
///
/// ```ignore
/// use georm::{Georm, Defaultable};
///
/// #[derive(Georm)]
/// #[georm(table = "posts")]
/// pub struct Post {
/// #[georm(id, defaultable)]
/// id: i32, // Auto-generated serial
/// title: String, // Required field
/// #[georm(defaultable)]
/// published: bool, // Database default: false
/// #[georm(defaultable)]
/// created_at: chrono::DateTime<chrono::Utc>, // Database default: NOW()
/// author_id: i32, // Required field
/// }
///
/// // Generated automatically:
/// // pub struct PostDefault {
/// // pub id: Option<i32>,
/// // pub title: String,
/// // pub published: Option<bool>,
/// // pub created_at: Option<chrono::DateTime<chrono::Utc>>,
/// // pub author_id: i32,
/// // }
/// //
/// // impl Defaultable<i32, Post> for PostDefault { ... }
///
/// // Create with some defaults
/// let post_default = PostDefault {
/// id: None, // Let database auto-generate
/// title: "My Blog Post".to_string(),
/// published: None, // Use database default (false)
/// created_at: None, // Use database default (NOW())
/// author_id: 42,
/// };
///
/// let created_post = post_default.create(&pool).await?;
/// println!("Created post with ID: {}", created_post.id);
///
/// // Create with explicit values
/// let post_default = PostDefault {
/// id: None, // Still auto-generate ID
/// title: "Published Post".to_string(),
/// published: Some(true), // Override default
/// created_at: Some(specific_time), // Override default
/// author_id: 42,
/// };
/// ```
///
/// ## Type Parameters
///
/// - `Id` - The primary key type of the target entity (e.g., `i32`, `UserRoleId`)
/// - `Entity` - The target entity type that will be created (e.g., `Post`, `User`)
///
/// ## Comparison with Regular Creation
///
/// ```ignore
/// // Using regular Georm::create - must provide all values
/// let post = Post {
/// id: 0, // Ignored for auto-increment, but required
/// title: "My Post".to_string(),
/// published: false, // Must specify even if it's the default
/// created_at: chrono::Utc::now(), // Must calculate current time manually
/// author_id: 42,
/// };
/// let created = post.create(&pool).await?;
///
/// // Using Defaultable::create - let database handle defaults
/// let post_default = PostDefault {
/// id: None, // Clearer intent for auto-generation
/// title: "My Post".to_string(),
/// published: None, // Let database default apply
/// created_at: None, // Let database calculate NOW()
/// author_id: 42,
/// };
/// let created = post_default.create(&pool).await?;
/// ```
///
/// ## Field Visibility
///
/// The generated companion struct preserves the field visibility of the original entity:
///
/// ```ignore
/// #[derive(Georm)]
/// #[georm(table = "posts")]
/// pub struct Post {
/// #[georm(id, defaultable)]
/// pub id: i32,
/// pub title: String,
/// #[georm(defaultable)]
/// pub(crate) internal_status: String, // Crate-private field
/// #[georm(defaultable)]
/// private_field: String, // Private field
/// }
///
/// // Generated with preserved visibility:
/// // pub struct PostDefault {
/// // pub id: Option<i32>,
/// // pub title: String,
/// // pub(crate) internal_status: Option<String>, // Preserved
/// // private_field: Option<String>, // Preserved
/// // }
/// ```
///
/// ## Limitations and Rules
///
/// - **Option fields cannot be defaultable**: Fields that are already `Option<T>` cannot
/// be marked with `#[georm(defaultable)]` to prevent `Option<Option<T>>` types
/// - **Compile-time validation**: Attempts to mark `Option<T>` fields as defaultable
/// result in compile-time errors
/// - **Requires at least one defaultable field**: The companion struct is only generated
/// if at least one field is marked as defaultable
/// - **No partial updates**: This trait only supports creating new entities, not updating
/// existing ones with defaults
///
/// ## Error Handling
///
/// The `create` method can fail for the same reasons as regular entity creation:
/// - Database connection issues
/// - Constraint violations (unique, foreign key, NOT NULL for non-defaultable fields)
/// - Permission problems
/// - Table or column doesn't exist
///
/// ## Performance Characteristics
///
/// - **Efficient SQL**: Only includes necessary fields in the INSERT statement
/// - **Single round-trip**: Uses `RETURNING *` to get the final entity state
/// - **No overhead**: Defaultable logic is resolved at compile time
/// - **Database-optimized**: Leverages database defaults rather than application logic
pub trait Defaultable<Id, Entity> {
/// Creates an entity in the database.
/// Create a new entity in the database using database defaults for unspecified fields.
///
/// # Errors
/// Returns any error the database may have encountered
/// This method constructs and executes an `INSERT INTO table_name (...) VALUES (...) RETURNING *`
/// query that only includes fields where `Some(value)` is provided. Fields that are `None`
/// are omitted from the query, allowing the database to apply default values, auto-increment
/// sequences, or trigger-generated values.
///
/// # Parameters
/// - `pool` - Database connection pool
///
/// # Returns
/// - `Ok(Entity)` - The newly created entity with all database-generated values populated
/// - `Err(sqlx::Error)` - Database constraint violations or connection errors
///
/// # Database Behavior
/// - **Selective field inclusion**: Only includes fields with `Some(value)` in the INSERT
/// - **Default value application**: Database defaults apply to omitted fields
/// - **RETURNING clause**: Captures the complete entity state after insertion
/// - **Trigger execution**: Database triggers run and their effects are captured
/// - **Sequence generation**: Auto-increment values are generated and returned
///
/// # SQL Generation
///
/// The generated SQL dynamically includes only the necessary fields:
///
/// ```sql
/// -- If id=None, published=None, created_at=None:
/// INSERT INTO posts (title, author_id) VALUES ($1, $2) RETURNING *;
///
/// -- If id=None, published=Some(true), created_at=None:
/// INSERT INTO posts (title, published, author_id) VALUES ($1, $2, $3) RETURNING *;
/// ```
///
/// # Examples
///
/// ```ignore
/// // Minimal creation - let database handle all defaults
/// let post_default = PostDefault {
/// id: None, // Auto-generated
/// title: "Hello World".to_string(),
/// published: None, // Database default
/// created_at: None, // Database default (NOW())
/// author_id: 1,
/// };
/// let post = post_default.create(&pool).await?;
///
/// // Mixed creation - some explicit values, some defaults
/// let post_default = PostDefault {
/// id: None, // Auto-generated
/// title: "Published Post".to_string(),
/// published: Some(true), // Override default
/// created_at: None, // Still use database default
/// author_id: 1,
/// };
/// let post = post_default.create(&pool).await?;
///
/// // Full control - specify all defaultable values
/// let specific_time = chrono::Utc::now() - chrono::Duration::hours(1);
/// let post_default = PostDefault {
/// id: Some(100), // Explicit ID (if not auto-increment)
/// title: "Backdated Post".to_string(),
/// published: Some(false), // Explicit value
/// created_at: Some(specific_time), // Explicit timestamp
/// author_id: 1,
/// };
/// let post = post_default.create(&pool).await?;
/// ```
///
/// # Error Conditions
///
/// Returns `sqlx::Error` for:
/// - **Unique constraint violations**: Duplicate values for unique fields
/// - **Foreign key violations**: Invalid references to other tables
/// - **NOT NULL violations**: Missing values for required non-defaultable fields
/// - **Check constraint violations**: Values that don't meet database constraints
/// - **Database connection issues**: Network or connection pool problems
/// - **Permission problems**: Insufficient privileges for the operation
/// - **Table/column errors**: Missing tables or columns (usually caught at compile time)
///
/// # Performance Notes
///
/// - **Optimal field selection**: Only transmits necessary data to the database
/// - **Single database round-trip**: INSERT and retrieval in one operation
/// - **Compile-time optimization**: Field inclusion logic resolved at compile time
/// - **Database-native defaults**: Leverages database performance for default value generation
///
/// # Comparison with Standard Creation
///
/// ```ignore
/// // Standard Georm::create - all fields required
/// let post = Post {
/// id: 0, // Placeholder for auto-increment
/// title: "My Post".to_string(),
/// published: false, // Must specify, even if it's the default
/// created_at: chrono::Utc::now(), // Must calculate manually
/// author_id: 1,
/// };
/// let created = post.create(&pool).await?;
///
/// // Defaultable::create - only specify what you need
/// let post_default = PostDefault {
/// id: None, // Clear intent for auto-generation
/// title: "My Post".to_string(),
/// published: None, // Let database decide
/// created_at: None, // Let database calculate
/// author_id: 1,
/// };
/// let created = post_default.create(&pool).await?;
/// ```
fn create(
&self,
pool: &sqlx::PgPool,
) -> impl std::future::Future<Output = sqlx::Result<Entity>> + Send;
) -> impl std::future::Future<Output = sqlx::Result<Entity>> + Send
where
Self: Sized;
}

View File

@ -1,18 +1,157 @@
/// Core database operations trait for Georm entities.
///
/// This trait is automatically implemented by the `#[derive(Georm)]` macro and provides
/// all essential CRUD operations for database entities. The trait is generic over the
/// primary key type `Id`, which can be a simple type (e.g., `i32`) or a generated
/// composite key struct (e.g., `UserRoleId`).
///
/// ## Generated Implementation
///
/// When you derive `Georm` on a struct, this trait is automatically implemented with
/// PostgreSQL-optimized queries that use:
/// - **Prepared statements** for security and performance
/// - **RETURNING clause** to capture database-generated values
/// - **ON CONFLICT** for efficient upsert operations
/// - **Compile-time verification** via SQLx macros
///
/// ## Method Categories
///
/// ### Static Methods (Query Operations)
/// - [`find_all`] - Retrieve all entities from the table
/// - [`find`] - Retrieve a single entity by primary key
/// - [`delete_by_id`] - Delete an entity by primary key
///
/// ### Instance Methods (Mutation Operations)
/// - [`create`] - Insert a new entity into the database
/// - [`update`] - Update an existing entity in the database
/// - [`create_or_update`] - Upsert (insert or update) an entity
/// - [`delete`] - Delete this entity from the database
/// - [`get_id`] - Get the primary key of this entity
///
/// ## Usage Examples
///
/// ```ignore
/// use georm::Georm;
///
/// #[derive(Georm)]
/// #[georm(table = "users")]
/// struct User {
/// #[georm(id)]
/// id: i32,
/// username: String,
/// email: String,
/// }
///
/// // Static methods
/// let all_users = User::find_all(&pool).await?;
/// let user = User::find(&pool, &1).await?;
/// let deleted_count = User::delete_by_id(&pool, &1).await?;
///
/// // Instance methods
/// let new_user = User { id: 0, username: "alice".into(), email: "alice@example.com".into() };
/// let created = new_user.create(&pool).await?;
/// let updated = created.update(&pool).await?;
/// let id = updated.get_id();
/// let deleted_count = updated.delete(&pool).await?;
/// ```
///
/// ## Composite Key Support
///
/// For entities with composite primary keys, the `Id` type parameter becomes a generated
/// struct following the pattern `{EntityName}Id`:
///
/// ```ignore
/// #[derive(Georm)]
/// #[georm(table = "user_roles")]
/// struct UserRole {
/// #[georm(id)]
/// user_id: i32,
/// #[georm(id)]
/// role_id: i32,
/// assigned_at: chrono::DateTime<chrono::Utc>,
/// }
///
/// // Generated: pub struct UserRoleId { pub user_id: i32, pub role_id: i32 }
/// // Trait: impl Georm<UserRoleId> for UserRole
///
/// let id = UserRoleId { user_id: 1, role_id: 2 };
/// let user_role = UserRole::find(&pool, &id).await?;
/// ```
///
/// ## Error Handling
///
/// All methods return `sqlx::Result<T>` and may fail due to:
/// - Database connection issues
/// - Constraint violations (unique, foreign key, etc.)
/// - Invalid queries (though most are caught at compile time)
/// - Missing records (for operations expecting existing data)
///
/// [`find_all`]: Georm::find_all
/// [`find`]: Georm::find
/// [`create`]: Georm::create
/// [`update`]: Georm::update
/// [`create_or_update`]: Georm::create_or_update
/// [`delete`]: Georm::delete
/// [`delete_by_id`]: Georm::delete_by_id
/// [`get_id`]: Georm::get_id
pub trait Georm<Id> {
/// Find all the entities in the database.
/// Retrieve all entities from the database table.
///
/// This method executes a `SELECT * FROM table_name` query and returns all records
/// as a vector of entities. The results are not paginated or filtered.
///
/// # Returns
/// - `Ok(Vec<Self>)` - All entities in the table (may be empty)
/// - `Err(sqlx::Error)` - Database connection or query execution errors
///
/// # Performance Notes
/// - Returns all records in memory - consider pagination for large tables
/// - Uses prepared statements for optimal performance
/// - No built-in ordering - results may vary between calls
///
/// # Examples
/// ```ignore
/// let all_users = User::find_all(&pool).await?;
/// println!("Found {} users", all_users.len());
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for database connection issues, permission problems,
/// or if the table doesn't exist.
fn find_all(
pool: &sqlx::PgPool,
) -> impl ::std::future::Future<Output = ::sqlx::Result<Vec<Self>>> + Send
where
Self: Sized;
/// Find the entiy in the database based on its identifier.
/// Find a single entity by its primary key.
///
/// This method executes a `SELECT * FROM table_name WHERE primary_key = $1` query
/// (or equivalent for composite keys) and returns the matching entity if found.
///
/// # Parameters
/// - `pool` - Database connection pool
/// - `id` - Primary key value (simple type or composite key struct)
///
/// # Returns
/// - `Ok(Some(Self))` - Entity found and returned
/// - `Ok(None)` - No entity with the given ID exists
/// - `Err(sqlx::Error)` - Database connection or query execution errors
///
/// # Examples
/// ```ignore
/// // Simple primary key
/// let user = User::find(&pool, &1).await?;
///
/// // Composite primary key
/// let id = UserRoleId { user_id: 1, role_id: 2 };
/// let user_role = UserRole::find(&pool, &id).await?;
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for database connection issues, type conversion errors,
/// or query execution problems. Note that not finding a record is not an error
/// - it returns `Ok(None)`.
fn find(
pool: &sqlx::PgPool,
id: &Id,
@ -20,10 +159,38 @@ pub trait Georm<Id> {
where
Self: Sized;
/// Create the entity in the database.
/// Insert this entity as a new record in the database.
///
/// This method executes an `INSERT INTO table_name (...) VALUES (...) RETURNING *`
/// query and returns the newly created entity with any database-generated values
/// (such as auto-increment IDs, default timestamps, etc.).
///
/// # Parameters
/// - `pool` - Database connection pool
///
/// # Returns
/// - `Ok(Self)` - The entity as it exists in the database after insertion
/// - `Err(sqlx::Error)` - Database constraint violations or connection errors
///
/// # Database Behavior
/// - Uses `RETURNING *` to capture database-generated values
/// - Respects database defaults for fields marked `#[georm(defaultable)]`
/// - Triggers and database-side modifications are reflected in the returned entity
///
/// # Examples
/// ```ignore
/// let new_user = User { id: 0, username: "alice".into(), email: "alice@example.com".into() };
/// let created_user = new_user.create(&pool).await?;
/// println!("Created user with ID: {}", created_user.id);
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for:
/// - Unique constraint violations
/// - Foreign key constraint violations
/// - NOT NULL constraint violations
/// - Database connection issues
/// - Permission problems
fn create(
&self,
pool: &sqlx::PgPool,
@ -31,10 +198,37 @@ pub trait Georm<Id> {
where
Self: Sized;
/// Update an entity with a matching identifier in the database.
/// Update an existing entity in the database.
///
/// This method executes an `UPDATE table_name SET ... WHERE primary_key = ... RETURNING *`
/// query using the entity's current primary key to locate the record to update.
///
/// # Parameters
/// - `pool` - Database connection pool
///
/// # Returns
/// - `Ok(Self)` - The entity as it exists in the database after the update
/// - `Err(sqlx::Error)` - Database errors or if no matching record exists
///
/// # Database Behavior
/// - Uses `RETURNING *` to capture any database-side changes
/// - Updates all fields, not just changed ones
/// - Triggers and database-side modifications are reflected in the returned entity
/// - Fails if no record with the current primary key exists
///
/// # Examples
/// ```ignore
/// let mut user = User::find(&pool, &1).await?.unwrap();
/// user.email = "newemail@example.com".into();
/// let updated_user = user.update(&pool).await?;
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for:
/// - No matching record found (record was deleted by another process)
/// - Constraint violations (unique, foreign key, etc.)
/// - Database connection issues
/// - Permission problems
fn update(
&self,
pool: &sqlx::PgPool,
@ -42,11 +236,37 @@ pub trait Georm<Id> {
where
Self: Sized;
/// Update an entity with a matching identifier in the database if
/// it exists, create it otherwise.
/// Insert or update this entity using PostgreSQL's upsert functionality.
///
/// This method executes an `INSERT ... ON CONFLICT (...) DO UPDATE SET ... RETURNING *`
/// query that atomically inserts the entity if it doesn't exist, or updates it if
/// a record with the same primary key already exists.
///
/// # Parameters
/// - `pool` - Database connection pool
///
/// # Returns
/// - `Ok(Self)` - The final entity state in the database (inserted or updated)
/// - `Err(sqlx::Error)` - Database connection or constraint violation errors
///
/// # Database Behavior
/// - Uses PostgreSQL's `ON CONFLICT` for true atomic upsert
/// - More efficient than separate find-then-create-or-update logic
/// - Uses `RETURNING *` to capture the final state
/// - Conflict resolution is based on the primary key constraint
///
/// # Examples
/// ```ignore
/// let user = User { id: 1, username: "alice".into(), email: "alice@example.com".into() };
/// let final_user = user.create_or_update(&pool).await?;
/// // Will insert if ID 1 doesn't exist, update if it does
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for:
/// - Non-primary-key constraint violations
/// - Database connection issues
/// - Permission problems
fn create_or_update(
&self,
pool: &sqlx::PgPool,
@ -54,30 +274,97 @@ pub trait Georm<Id> {
where
Self: Sized;
/// Delete the entity from the database if it exists.
/// Delete this entity from the database.
///
/// This method executes a `DELETE FROM table_name WHERE primary_key = ...` query
/// using this entity's primary key to identify the record to delete.
///
/// # Parameters
/// - `pool` - Database connection pool
///
/// # Returns
/// Returns the amount of rows affected by the deletion.
/// - `Ok(u64)` - Number of rows affected (0 if entity didn't exist, 1 if deleted)
/// - `Err(sqlx::Error)` - Database connection or constraint violation errors
///
/// # Database Behavior
/// - Uses the entity's current primary key for deletion
/// - Returns 0 if no matching record exists (not an error)
/// - May fail due to foreign key constraints if other records reference this entity
///
/// # Examples
/// ```ignore
/// let user = User::find(&pool, &1).await?.unwrap();
/// let deleted_count = user.delete(&pool).await?;
/// assert_eq!(deleted_count, 1);
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for:
/// - Foreign key constraint violations (referenced by other tables)
/// - Database connection issues
/// - Permission problems
fn delete(
&self,
pool: &sqlx::PgPool,
) -> impl std::future::Future<Output = sqlx::Result<u64>> + Send;
/// Delete any entity with the identifier `id`.
/// Delete an entity by its primary key without needing an entity instance.
///
/// This method executes a `DELETE FROM table_name WHERE primary_key = ...` query
/// using the provided ID to identify the record to delete.
///
/// # Parameters
/// - `pool` - Database connection pool
/// - `id` - Primary key value (simple type or composite key struct)
///
/// # Returns
/// Returns the amount of rows affected by the deletion.
/// - `Ok(u64)` - Number of rows affected (0 if entity didn't exist, 1 if deleted)
/// - `Err(sqlx::Error)` - Database connection or constraint violation errors
///
/// # Database Behavior
/// - More efficient than `find().delete()` when you only have the ID
/// - Returns 0 if no matching record exists (not an error)
/// - May fail due to foreign key constraints if other records reference this entity
///
/// # Examples
/// ```ignore
/// // Simple primary key
/// let deleted_count = User::delete_by_id(&pool, &1).await?;
///
/// // Composite primary key
/// let id = UserRoleId { user_id: 1, role_id: 2 };
/// let deleted_count = UserRole::delete_by_id(&pool, &id).await?;
/// ```
///
/// # Errors
/// Returns any error Postgres may have encountered
/// Returns `sqlx::Error` for:
/// - Foreign key constraint violations (referenced by other tables)
/// - Database connection issues
/// - Permission problems
fn delete_by_id(
pool: &sqlx::PgPool,
id: &Id,
) -> impl std::future::Future<Output = sqlx::Result<u64>> + Send;
/// Returns the identifier of the entity.
/// Get the primary key of this entity.
///
/// For entities with simple primary keys, this returns the ID value directly.
/// For entities with composite primary keys, this returns an owned instance of
/// the generated `{EntityName}Id` struct.
///
/// # Returns
/// - Simple keys: The primary key value (e.g., `i32`, `String`)
/// - Composite keys: Generated ID struct (e.g., `UserRoleId`)
///
/// # Examples
/// ```ignore
/// // Simple primary key
/// let user = User { id: 42, username: "alice".into(), email: "alice@example.com".into() };
/// let id = user.get_id(); // Returns 42
///
/// // Composite primary key
/// let user_role = UserRole { user_id: 1, role_id: 2, assigned_at: now };
/// let id = user_role.get_id(); // Returns UserRoleId { user_id: 1, role_id: 2 }
/// ```
fn get_id(&self) -> Id;
}

View File

@ -1,281 +1,135 @@
//! # Georm
//!
//! ## Introduction
//! A simple, type-safe PostgreSQL ORM built on SQLx with zero runtime overhead.
//!
//! Georm is a simple, opinionated SQLx ORM for PostgreSQL.
//!
//! To automatically implement the `Georm` trait, you need at least:
//! - to derive the `Georm` and `sqlx::FromRow` traits
//! - use the `georm` proc-macro to indicate the table in which your entity
//! lives
//! - use the `georm` proc-macro again to indicate which field of your struct is
//! the identifier of your entity.
//!
//! ## Simple usage
//! Here is a minimal use of Georm with a struct:
//! ## Quick Start
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! use georm::Georm;
//!
//! // Note: No need to derive FromRow - Georm generates it automatically
//! #[derive(Georm)]
//! #[georm(table = "users")]
//! pub struct User {
//! #[georm(id)]
//! id: i32,
//! username: String,
//! hashed_password: String,
//! email: String,
//! }
//!
//! // Use generated methods
//! let user = User::find(&pool, &1).await?; // Static method
//! let all_users = User::find_all(&pool).await?; // Static method
//! user.update(&pool).await?; // Instance method
//! ```
//!
//! The `User` type will now have access to all the functions declared in the
//! `Georm` trait.
//! ## Core CRUD Operations
//!
//! ## One-to-one relationships
//! ### Static Methods (called on the struct type)
//! - `Entity::find(pool, &id)` - Find by primary key, returns `Option<Entity>`
//! - `Entity::find_all(pool)` - Get all records, returns `Vec<Entity>`
//! - `Entity::delete_by_id(pool, &id)` - Delete by ID, returns affected row count
//!
//! You can then create relationships between different entities. For instance,
//! you can use an identifier of another entity as a link to that other entity.
//! ### Instance Methods (called on entity objects)
//! - `entity.create(pool)` - Insert new record, returns created entity with database-generated values
//! - `entity.update(pool)` - Update existing record, returns updated entity with fresh database state
//! - `entity.create_or_update(pool)` - True PostgreSQL upsert using `ON CONFLICT`, returns final entity
//! - `entity.delete(pool)` - Delete this record, returns affected row count
//! - `entity.get_id()` - Get reference to the entity's ID (`&Id` for simple keys, owned for composite)
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(table = "profiles")]
//! pub struct Profile {
//! // Static methods
//! let user = User::find(&pool, &1).await?.unwrap();
//! let all_users = User::find_all(&pool).await?;
//! let deleted_count = User::delete_by_id(&pool, &1).await?;
//!
//! // Instance methods
//! let new_user = User { id: 0, username: "alice".to_string(), email: "alice@example.com".to_string() };
//! let created = new_user.create(&pool).await?; // Returns entity with actual generated ID
//! let updated = created.update(&pool).await?; // Returns entity with fresh database state
//! let deleted_count = updated.delete(&pool).await?;
//! ```
//!
//! ### PostgreSQL Optimizations
//!
//! Georm leverages PostgreSQL-specific features for performance and reliability:
//!
//! - **RETURNING clause**: All `INSERT` and `UPDATE` operations use `RETURNING *` to capture database-generated values (sequences, defaults, triggers)
//! - **True upserts**: `create_or_update()` uses `INSERT ... ON CONFLICT ... DO UPDATE` for atomic upsert operations
//! - **Prepared statements**: All queries use parameter binding for security and performance
//! - **Compile-time verification**: SQLx macros verify all generated SQL against your database schema at compile time
//!
//! ## Primary Keys and Identifiers
//!
//! ### Simple Primary Keys
//!
//! Primary key fields can have any name (not just "id"):
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(table = "books")]
//! pub struct Book {
//! #[georm(id)]
//! ident: i32, // Custom field name for primary key
//! title: String,
//! }
//!
//! // Works the same way
//! let book = Book::find(&pool, &1).await?;
//! ```
//!
//! ### Composite Primary Keys
//!
//! Mark multiple fields with `#[georm(id)]` for composite keys:
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(table = "user_roles")]
//! pub struct UserRole {
//! #[georm(id)]
//! id: i32,
//! #[georm(
//! relation = {
//! entity = User,
//! name = "user",
//! table = "users",
//! remote_id = "id",
//! nullable = false
//! })
//! ]
//! user_id: i32,
//! display_name: String,
//! #[georm(id)]
//! role_id: i32,
//! assigned_at: chrono::DateTime<chrono::Utc>,
//! }
//! ```
//!
//! This will give access to the `Profile::get_user(&self, pool: &sqlx::PgPool)
//! -> User` method.
//!
//! Here is an explanation of what these different values mean:
//!
//! | Value Name | Explanation | Default value |
//! |------------|------------------------------------------------------------------------------------------|---------------|
//! | entity | Rust type of the entity found in the database | N/A |
//! | name | Name of the remote entity within the local entity; generates a method named `get_{name}` | N/A |
//! | table | Database table where the entity is stored | N/A |
//! | remote_id | Name of the column serving as the identifier of the entity | `"id"` |
//! | nullable | Whether the relationship can be broken | `false` |
//!
//! Note that in this instance, the `remote_id` and `nullable` values can be
//! omitted as this is their default value. This below is a strict equivalent:
//! This automatically generates a composite ID struct following the `{EntityName}Id` pattern:
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(table = "profiles")]
//! pub struct Profile {
//! #[georm(id)]
//! id: i32,
//! #[georm(relation = { entity = User, table = "users", name = "user" })]
//! user_id: i32,
//! display_name: String,
//! // Generated automatically by the macro
//! pub struct UserRoleId {
//! pub user_id: i32,
//! pub role_id: i32,
//! }
//! ```
//!
//! But what if I have a one-to-one relationship with another entity and
//! my current entity holds no data to reference that other identity? No
//! worries, there is another way to declare such relationships.
//! Usage with composite keys:
//!
//! ```ignore
//! #[georm(
//! one_to_one = [{
//! name = "profile",
//! remote_id = "user_id",
//! table = "profiles",
//! entity = User
//! }]
//! )]
//! struct User {
//! #[georm(id)]
//! id: i32,
//! username: String,
//! hashed_password: String,
//! }
//! // Static methods work with generated ID structs
//! let id = UserRoleId { user_id: 1, role_id: 2 };
//! let user_role = UserRole::find(&pool, &id).await?;
//! UserRole::delete_by_id(&pool, &id).await?;
//!
//! // Instance methods work the same way
//! let role = UserRole { user_id: 1, role_id: 2, assigned_at: chrono::Utc::now() };
//! let created = role.create(&pool).await?;
//! let id = created.get_id(); // Returns owned UserRoleId for composite keys
//! ```
//!
//! We now have access to the method `User::get_profile(&self, &pool:
//! sqlx::PgPool) -> Option<User>`.
//! ### Composite Key Limitations
//!
//! Here is an explanation of the values of `one_to_many`:
//!
//! | Value Name | Explanaion | Default Value |
//! |------------|------------------------------------------------------------------------------------------|---------------|
//! | entity | Rust type of the entity found in the database | N/A |
//! | name | Name of the remote entity within the local entity; generates a method named `get_{name}` | N/A |
//! | table | Database table where the entity is stored | N/A |
//! | remote_id | Name of the column serving as the identifier of the entity | `"id"` |
//!
//! ## One-to-many relationships
//!
//! Sometimes, our entity is the one being referenced to by multiple entities,
//! but we have no internal reference to these remote entities in our local
//! entity. Fortunately, we have a way to indicate to Georm how to find these.
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(table = "posts")]
//! struct Post {
//! #[georm(id)]
//! id: i32,
//! #[georm(relation = { entity = User, table = "users", name = "user" })]
//! author_id: i32,
//! content: String
//! }
//!
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(
//! table = "users",
//! one_to_many = [{
//! entity = Post,
//! name = "posts",
//! table = "posts",
//! remote_id = "author_id"
//! }]
//! )]
//! struct User {
//! #[georm(id)]
//! id: i32,
//! username: String,
//! hashed_password: String
//! }
//! ```
//!
//! As weve seen earlier, the struct `Post` has access to the method
//! `Post::get_user(&self, pool: &sqlx::PgPool) -> User` thanks to the
//! proc-macro used on `author_id`. However, `User` now has also access to
//! `User::get_posts(&self, pool: &sqlx::PgPool) -> Vec<Post>`. And as you can
//! see, `one_to_many` is an array, meaning you can define several one-to-many
//! relationships for `User`.
//!
//! Here is an explanation of the values of `one_to_many`:
//!
//! | Value Name | Explanaion | Default Value |
//! |------------|------------------------------------------------------------------------------------------|---------------|
//! | entity | Rust type of the entity found in the database | N/A |
//! | name | Name of the remote entity within the local entity; generates a method named `get_{name}` | N/A |
//! | table | Database table where the entity is stored | N/A |
//! | remote_id | Name of the column serving as the identifier of the entity | `"id"` |
//!
//! As with one-to-one relationships, `remote_id` is optional. The following
//! `User` struct is strictly equivalent.
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(
//! table = "users",
//! one_to_many = [{ entity = Post, name = "posts", table = "posts" }]
//! )]
//! struct User {
//! #[georm(id)]
//! id: i32,
//! username: String,
//! hashed_password: String
//! }
//! ```
//!
//! ## Many-to-many relationships
//!
//! Many-to-many relationships between entities A and entities B with Georm rely
//! on a third table which refers to both. For instance, the following SQL code
//! describes a many-to-many relationship between books and book genre.
//!
//! ```sql
//! CREATE TABLE books (
//! id SERIAL PRIMARY KEY,
//! title VARCHAR(100) NOT NULL
//! );
//!
//! CREATE TABLE genres (
//! id SERIAL PRIMARY KEY,
//! name VARCHAR(100) NOT NULL
//! );
//!
//! CREATE TABLE books_genres (
//! book_id INT NOT NULL,
//! genre_id INT NOT NULL,
//! PRIMARY KEY (book_id, genre_id),
//! FOREIGN KEY (book_id) REFERENCES books(id) ON DELETE CASCADE,
//! FOREIGN KEY (genre_id) REFERENCES genres(id) ON DELETE CASCADE
//! );
//! ```
//!
//! The table `books_genres` is the one defining the many-to-many relationship
//! between the table `books` and the table `genres`. With Georm, this gives us
//! the following code:
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(
//! table = "books",
//! many_to_many = [{
//! name = "genres",
//! entity = Genre,
//! table = "genres",
//! remote_id = "id",
//! link = { table = "books_genres", from = "book_id", to = "genre_id" }
//! }]
//! )]
//! struct Book {
//! #[georm(id)]
//! id: i32,
//! title: String
//! }
//!
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(
//! table = "genres",
//! many_to_many = [{
//! entity = Book,
//! name = "books",
//! table = "books",
//! remote_id = "id",
//! link = { table = "books_genres", from = "genre_id", to = "book_id" }
//! }]
//! )]
//! struct Genre {
//! #[georm(id)]
//! id: i32,
//! name: String
//! }
//! ```
//!
//! This generates two methods:
//! - `Book::get_genres(&self, pool: &sqlx::PgPool) -> Vec<Genre>`
//! - `Genre::get_books(&self, pool: &sqlx::PgPool) -> Vec<Book>`
//!
//! As you can see, `many_to_many` is also an array, meaning we can define
//! several many-to-many relationships for the same struct.
//!
//! Here is an explanation of the values behind `many_to_many`:
//!
//! | Value Name | Explanation | Default value |
//! |------------|------------------------------------------------------------------------------------------|---------------|
//! | entity | Rust type of the entity found in the database | N/A |
//! | name | Name of the remote entity within the local entity; generates a method named `get_{name}` | N/A |
//! | table | Database table where the entity is stored | N/A |
//! | remote_id | Name of the column serving as the identifier of the entity | `"id"` |
//! | link.table | Name of the many-to-many relationship table | N/A |
//! | link.from | Column of the linking table referring to this entity | N/A |
//! | link.to | Column of the linking table referring to the remote entity | N/A |
//! - **Relationships not supported**: Entities with composite primary keys cannot
//! yet define relationships (one-to-one, one-to-many, many-to-many)
//! - **ID struct naming**: Generated ID struct follows pattern `{EntityName}Id` (not customizable)
//!
//! ## Defaultable Fields
//!
//! Georm supports defaultable fields for entities where some fields have database
//! defaults or are auto-generated (like serial IDs). When you mark fields as
//! `defaultable`, Georm generates a companion struct that makes these fields
//! optional during entity creation.
//! Use `#[georm(defaultable)]` for fields with database defaults or auto-generated values:
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[derive(Georm)]
//! #[georm(table = "posts")]
//! pub struct Post {
//! #[georm(id, defaultable)]
@ -284,12 +138,14 @@
//! #[georm(defaultable)]
//! published: bool, // Has database default
//! #[georm(defaultable)]
//! created_at: chrono::DateTime<chrono::Utc>, // Has database default
//! created_at: chrono::DateTime<chrono::Utc>, // DEFAULT NOW()
//! #[georm(defaultable)]
//! pub(crate) internal_note: String, // Field visibility preserved
//! author_id: i32, // Required field
//! }
//! ```
//!
//! This generates a `PostDefault` struct where defaultable fields become `Option<T>`:
//! This generates a companion `PostDefault` struct where defaultable fields become `Option<T>`:
//!
//! ```ignore
//! // Generated automatically by the macro
@ -298,6 +154,7 @@
//! pub title: String, // Required field stays the same
//! pub published: Option<bool>, // Can be None to use database default
//! pub created_at: Option<chrono::DateTime<chrono::Utc>>, // Can be None
//! pub(crate) internal_note: Option<String>, // Visibility preserved
//! pub author_id: i32, // Required field stays the same
//! }
//!
@ -317,90 +174,412 @@
//! title: "My Blog Post".to_string(),
//! published: None, // Use database default (e.g., false)
//! created_at: None, // Use database default (e.g., NOW())
//! internal_note: Some("Draft".to_string()),
//! author_id: 42,
//! };
//!
//! // Create the entity in the database
//! // Create the entity in the database (instance method on PostDefault)
//! let created_post = post_default.create(&pool).await?;
//! println!("Created post with ID: {}", created_post.id);
//! ```
//!
//! ### Rules and Limitations
//! ### Defaultable Rules and Limitations
//!
//! - **Option fields cannot be marked as defaultable**: If a field is already
//! `Option<T>`, you cannot mark it with `#[georm(defaultable)]`. This prevents
//! `Option<Option<T>>` types.
//! `Option<Option<T>>` types and causes a compile-time error.
//! - **Field visibility is preserved**: The generated defaultable struct maintains
//! the same field visibility (`pub`, `pub(crate)`, private) as the original
//! struct.
//! - **ID fields can be defaultable**: It's common to mark ID fields as
//! defaultable when they are auto-generated serials in PostgreSQL.
//! the same field visibility (`pub`, `pub(crate)`, private) as the original struct.
//! - **ID fields can be defaultable**: It's common to mark ID fields as defaultable
//! when they are auto-generated serials in PostgreSQL.
//! - **Only generates when needed**: The defaultable struct is only generated if
//! at least one field is marked as defaultable.
//!
//! ## Composite Primary Keys
//! ## Relationships
//!
//! Georm supports composite primary keys by marking multiple fields with
//! `#[georm(id)]`:
//! Georm supports comprehensive relationship modeling with two approaches: field-level
//! relationships for foreign keys and struct-level relationships for reverse lookups.
//! Each relationship method call executes a separate database query.
//!
//! ### Field-Level Relationships (Foreign Keys)
//!
//! Use the `relation` attribute on foreign key fields to generate lookup methods:
//!
//! ```ignore
//! #[derive(sqlx::FromRow, Georm)]
//! #[georm(table = "user_roles")]
//! pub struct UserRole {
//! #[derive(Georm)]
//! #[georm(table = "posts")]
//! pub struct Post {
//! #[georm(id)]
//! user_id: i32,
//! #[georm(id)]
//! role_id: i32,
//! assigned_at: chrono::DateTime<chrono::Utc>,
//! id: i32,
//! title: String,
//! #[georm(relation = {
//! entity = Author, // Target entity type
//! table = "authors", // Target table name
//! name = "author", // Method name (generates get_author)
//! remote_id = "id", // Target table's key column (default: "id")
//! nullable = false // Whether relationship can be null (default: false)
//! })]
//! author_id: i32,
//! }
//! ```
//!
//! When multiple fields are marked as ID fields, Georm automatically generates a
//! composite ID struct:
//! **Generated instance method**: `post.get_author(pool).await? -> sqlx::Result<Author>`
//!
//! For nullable relationships:
//!
//! ```ignore
//! // Generated automatically by the macro
//! pub struct UserRoleId {
//! pub user_id: i32,
//! pub role_id: i32,
//! #[derive(Georm)]
//! #[georm(table = "posts")]
//! pub struct Post {
//! #[georm(id)]
//! id: i32,
//! title: String,
//! #[georm(relation = {
//! entity = Category,
//! table = "categories",
//! name = "category",
//! nullable = true // Allows NULL values
//! })]
//! category_id: Option<i32>,
//! }
//! ```
//!
//! This allows you to use the generated ID struct with all Georm methods:
//! **Generated instance method**: `post.get_category(pool).await? -> sqlx::Result<Option<Category>>`
//!
//! Since `remote_id` and `nullable` have default values, this is equivalent:
//!
//! ```ignore
//! // Find by composite key
//! let id = UserRoleId { user_id: 1, role_id: 2 };
//! let user_role = UserRole::find(&pool, &id).await?;
//!
//! // Delete by composite key
//! UserRole::delete_by_id(&pool, &id).await?;
//!
//! // Get composite ID from instance
//! let user_role = UserRole { user_id: 1, role_id: 2, assigned_at: chrono::Utc::now() };
//! let id = user_role.get_id(); // Returns UserRoleId
//! #[georm(relation = { entity = Author, table = "authors", name = "author" })]
//! author_id: i32,
//! ```
//!
//! ### Composite Key Limitations
//! #### Non-Standard Primary Key References
//!
//! - **Relationships not supported**: Entities with composite primary keys cannot
//! yet define relationships (one-to-one, one-to-many, many-to-many) as those
//! features require single-field primary keys.
//! - **ID struct naming**: The generated ID struct follows the pattern
//! `{EntityName}Id`.
//! Use `remote_id` to reference tables with non-standard primary key names:
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(table = "reviews")]
//! pub struct Review {
//! #[georm(id)]
//! id: i32,
//! #[georm(relation = {
//! entity = Book,
//! table = "books",
//! name = "book",
//! remote_id = "ident" // Book uses 'ident' instead of 'id'
//! })]
//! book_id: i32,
//! content: String,
//! }
//! ```
//!
//! #### Field-Level Relationship Attributes
//!
//! | Attribute | Description | Required | Default |
//! |--------------|------------------------------------------------------|----------|---------|
//! | `entity` | Target entity type | Yes | N/A |
//! | `name` | Method name (generates `get_{name}`) | Yes | N/A |
//! | `table` | Target table name | Yes | N/A |
//! | `remote_id` | Target table's key column | No | `"id"` |
//! | `nullable` | Whether relationship can be null | No | `false` |
//!
//! ### Struct-Level Relationships (Reverse Lookups)
//!
//! Define relationships at the struct level to query related entities that reference this entity.
//! These generate separate database queries for each method call.
//!
//! #### One-to-One Relationships
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(
//! table = "users",
//! one_to_one = [{
//! entity = Profile, // Related entity type
//! name = "profile", // Method name (generates get_profile)
//! table = "profiles", // Related table name
//! remote_id = "user_id", // Foreign key in related table
//! }]
//! )]
//! pub struct User {
//! #[georm(id)]
//! id: i32,
//! username: String,
//! }
//! ```
//!
//! **Generated instance method**: `user.get_profile(pool).await? -> sqlx::Result<Option<Profile>>`
//!
//! #### One-to-Many Relationships
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(
//! table = "authors",
//! one_to_many = [{
//! entity = Post, // Related entity type
//! name = "posts", // Method name (generates get_posts)
//! table = "posts", // Related table name
//! remote_id = "author_id" // Foreign key in related table
//! }, {
//! entity = Comment, // Multiple relationships allowed
//! name = "comments",
//! table = "comments",
//! remote_id = "author_id"
//! }]
//! )]
//! pub struct Author {
//! #[georm(id)]
//! id: i32,
//! name: String,
//! }
//! ```
//!
//! **Generated instance methods**:
//! - `author.get_posts(pool).await? -> sqlx::Result<Vec<Post>>`
//! - `author.get_comments(pool).await? -> sqlx::Result<Vec<Comment>>`
//!
//! #### Many-to-Many Relationships
//!
//! For many-to-many relationships, specify the link table that connects the entities:
//!
//! ```sql
//! -- Example schema for books and genres
//! CREATE TABLE books (
//! id SERIAL PRIMARY KEY,
//! title VARCHAR(200) NOT NULL
//! );
//!
//! CREATE TABLE genres (
//! id SERIAL PRIMARY KEY,
//! name VARCHAR(100) NOT NULL
//! );
//!
//! CREATE TABLE book_genres (
//! book_id INT NOT NULL REFERENCES books(id),
//! genre_id INT NOT NULL REFERENCES genres(id),
//! PRIMARY KEY (book_id, genre_id)
//! );
//! ```
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(
//! table = "books",
//! many_to_many = [{
//! entity = Genre, // Related entity type
//! name = "genres", // Method name (generates get_genres)
//! table = "genres", // Related table name
//! remote_id = "id", // Primary key in related table (default: "id")
//! link = { // Link table configuration
//! table = "book_genres", // Join table name
//! from = "book_id", // Column referencing this entity
//! to = "genre_id" // Column referencing related entity
//! }
//! }]
//! )]
//! pub struct Book {
//! #[georm(id)]
//! id: i32,
//! title: String,
//! }
//!
//! #[derive(Georm)]
//! #[georm(
//! table = "genres",
//! many_to_many = [{
//! entity = Book,
//! name = "books",
//! table = "books",
//! link = {
//! table = "book_genres",
//! from = "genre_id", // Note: reversed perspective
//! to = "book_id"
//! }
//! }]
//! )]
//! pub struct Genre {
//! #[georm(id)]
//! id: i32,
//! name: String,
//! }
//! ```
//!
//! **Generated instance methods**:
//! - `book.get_genres(pool).await? -> sqlx::Result<Vec<Genre>>`
//! - `genre.get_books(pool).await? -> sqlx::Result<Vec<Book>>`
//!
//! #### Struct-Level Relationship Attributes
//!
//! | Attribute | Description | Required | Default |
//! |--------------|------------------------------------------------------|----------|---------|
//! | `entity` | Target entity type | Yes | N/A |
//! | `name` | Method name (generates `get_{name}`) | Yes | N/A |
//! | `table` | Target table name | Yes | N/A |
//! | `remote_id` | Target table's key column | No | `"id"` |
//! | `link.table` | Join table name (many-to-many only) | Yes* | N/A |
//! | `link.from` | Column referencing this entity (many-to-many only) | Yes* | N/A |
//! | `link.to` | Column referencing target entity (many-to-many only) | Yes* | N/A |
//!
//! *Required for many-to-many relationships
//!
//! As with field-level relationships, `remote_id` is optional and defaults to `"id"`:
//!
//! ```ignore
//! #[georm(
//! table = "users",
//! one_to_many = [{ entity = Post, name = "posts", table = "posts" }]
//! )]
//! ```
//!
//! #### Complex Relationship Example
//!
//! Here's a comprehensive example showing multiple relationship types:
//!
//! ```ignore
//! #[derive(Georm)]
//! #[georm(
//! table = "posts",
//! one_to_many = [{
//! entity = Comment,
//! name = "comments",
//! table = "comments",
//! remote_id = "post_id"
//! }],
//! many_to_many = [{
//! entity = Tag,
//! name = "tags",
//! table = "tags",
//! link = {
//! table = "post_tags",
//! from = "post_id",
//! to = "tag_id"
//! }
//! }]
//! )]
//! pub struct Post {
//! #[georm(id)]
//! id: i32,
//! title: String,
//! content: String,
//!
//! // Field-level relationship (foreign key)
//! #[georm(relation = {
//! entity = Author,
//! table = "authors",
//! name = "author"
//! })]
//! author_id: i32,
//!
//! // Nullable field-level relationship
//! #[georm(relation = {
//! entity = Category,
//! table = "categories",
//! name = "category",
//! nullable = true
//! })]
//! category_id: Option<i32>,
//! }
//! ```
//!
//! **Generated instance methods**:
//! - `post.get_author(pool).await? -> sqlx::Result<Author>` (from field relation)
//! - `post.get_category(pool).await? -> sqlx::Result<Option<Category>>` (nullable field relation)
//! - `post.get_comments(pool).await? -> sqlx::Result<Vec<Comment>>` (one-to-many)
//! - `post.get_tags(pool).await? -> sqlx::Result<Vec<Tag>>` (many-to-many)
//!
//! ## Error Handling
//!
//! All Georm methods return `sqlx::Result<T>` which can contain:
//!
//! - **Database errors**: Connection issues, constraint violations, etc.
//! - **Not found errors**: When `find()` operations return `None`
//! - **Compile-time errors**: Invalid SQL, type mismatches, schema validation failures
//!
//! ### Compile-Time Validations
//!
//! Georm performs several validations at compile time:
//!
//! ```ignore
//! // ❌ Compile error: No ID field specified
//! #[derive(Georm)]
//! #[georm(table = "invalid")]
//! pub struct Invalid {
//! name: String, // Missing #[georm(id)]
//! }
//!
//! // ❌ Compile error: Option<T> cannot be defaultable
//! #[derive(Georm)]
//! #[georm(table = "invalid")]
//! pub struct Invalid {
//! #[georm(id)]
//! id: i32,
//! #[georm(defaultable)] // Error: would create Option<Option<String>>
//! optional_field: Option<String>,
//! }
//! ```
//!
//! ## Attribute Reference
//!
//! ### Struct-Level Attributes
//!
//! ```ignore
//! #[georm(
//! table = "table_name", // Required: database table name
//! one_to_one = [{ /* ... */ }], // Optional: one-to-one relationships
//! one_to_many = [{ /* ... */ }], // Optional: one-to-many relationships
//! many_to_many = [{ /* ... */ }] // Optional: many-to-many relationships
//! )]
//! ```
//!
//! ### Field-Level Attributes
//!
//! ```ignore
//! #[georm(id)] // Mark as primary key (required on at least one field)
//! #[georm(defaultable)] // Mark as defaultable field (database default/auto-generated)
//! #[georm(relation = { /* ... */ })] // Define foreign key relationship
//! ```
//!
//! ## Performance Characteristics
//!
//! - **Zero runtime overhead**: All SQL is generated at compile time
//! - **No eager loading**: Each relationship method executes a separate query
//! - **Prepared statements**: All queries use parameter binding for optimal performance
//! - **Database round-trips**: CRUD operations use RETURNING clause to minimize round-trips
//! - **No N+1 prevention**: Built-in relationships don't prevent N+1 query patterns
//!
//! ## Limitations
//! ### Database
//!
//! For now, Georm is limited to PostgreSQL. Other databases may be supported in
//! the future, such as Sqlite or MySQL, but that is not the case yet.
//! ### Database Support
//!
//! ## Identifiers
//! Georm is currently limited to PostgreSQL. Other databases may be supported in
//! the future, such as SQLite or MySQL, but that is not the case yet.
//!
//! ### Identifiers
//!
//! Identifiers, or primary keys from the point of view of the database, may
//! be simple types recognized by SQLx or composite keys (multiple fields marked
//! with `#[georm(id)]`). Single primary keys cannot be arrays, and optionals are
//! only supported in one-to-one relationships when explicitly marked as nullables.
//!
//! ### Current Limitations
//!
//! - **Composite key relationships**: Entities with composite primary keys cannot define relationships
//! - **Single table per entity**: No table inheritance or polymorphism support
//! - **No advanced queries**: No complex WHERE clauses or joins beyond relationships
//! - **No eager loading**: Each relationship call is a separate database query
//! - **No field-based queries**: No `find_by_{field_name}` methods generated automatically
//! - **PostgreSQL only**: No support for other database systems
//!
//! ## Generated Code
//!
//! Georm automatically generates:
//! - `sqlx::FromRow` implementation (no need to derive manually)
//! - Composite ID structs for multi-field primary keys
//! - Defaultable companion structs for entities with defaultable fields
//! - Relationship methods for accessing related entities
//! - All CRUD operations with proper PostgreSQL optimizations
pub use georm_macros::Georm;