lume 0.13.1

A simple and intuitive Query Builder inspired by Drizzle
Documentation
#![warn(missing_docs)]

//! # Table Module
//!
//! This module provides table registry functionality and the `TableDefinition` trait
//! for managing database table metadata and SQL generation.

use std::{
    fmt::Debug,
    sync::{Mutex, OnceLock},
};

use crate::schema::{ColumnInfo, Schema, SchemaWrapper};

/// Global table registry for storing all registered tables
static TABLE_REGISTRY: OnceLock<Mutex<Vec<Box<dyn TableDefinition>>>> = OnceLock::new();

/// Trait for defining database table metadata and SQL generation.
///
/// This trait provides the interface for table definitions, including
/// table names, column metadata, and SQL generation for CREATE TABLE statements.
///
/// # Features
///
/// - **Table Metadata**: Access to table names and column information
/// - **SQL Generation**: Automatic CREATE TABLE statement generation
/// - **Index Creation**: Support for automatic index creation
/// - **Constraint Handling**: Primary keys, unique constraints, and NOT NULL
///
/// # Example
pub(crate) trait TableDefinition: Send + Sync + Debug {
    /// Returns the name of this table.
    fn table_name(&self) -> &'static str;

    /// Returns metadata for all columns in this table.
    fn get_columns(&self) -> Vec<ColumnInfo<'static>>;

    /// Generates the CREATE TABLE SQL statement for this table.
    ///
    /// This includes all columns, constraints, and indexes.
    fn to_create_sql(&self) -> String;

    /// Creates a boxed clone of this table definition.
    fn clone_box(&self) -> Box<dyn TableDefinition>;
}

/// Registers a schema type in the global table registry.
///
/// This function is idempotent - calling it multiple times with the same
/// schema type will not create duplicate entries.
///
/// # Arguments
///
/// - `T`: The schema type to register (must implement `Schema + Send + Sync + 'static`)
///
/// # Example
///
/// ```rust
/// use lume::define_schema;
/// use lume::schema::{Schema, ColumnInfo};
///
/// define_schema! {
///     User {
///         id: i32 [primary_key()],
///         name: String [not_null()],
///     }
/// }
///
/// // Register the table
/// lume::table::register_table::<User>();
///
/// // Multiple calls are safe
/// lume::table::register_table::<User>(); // No duplicate created
/// ```
pub fn register_table<T: Debug + Schema + Send + Sync + 'static>() {
    let registry = TABLE_REGISTRY.get_or_init(|| Mutex::new(Vec::new()));
    let mut tables = registry.lock().unwrap();

    // Check if table already registered; return early to keep idempotent
    let table_name = T::table_name();
    let already_exists = tables.iter().any(|t| t.table_name() == table_name);
    if already_exists {
        return;
    }

    tables.push(Box::new(SchemaWrapper::<T>::new()));
}

/// Returns all registered tables as a vector of boxed `TableDefinition` instances.
///
/// # Returns
///
/// A vector containing all registered table definitions
pub(crate) fn get_all_tables() -> Vec<Box<dyn TableDefinition>> {
    let registry = TABLE_REGISTRY.get_or_init(|| Mutex::new(Vec::new()));
    let tables = registry.lock().unwrap();
    tables.iter().map(|t| t.clone_box()).collect()
}