entity-derive-impl 0.3.0

Internal proc-macro implementation for entity-derive. Use entity-derive instead.
Documentation
// SPDX-FileCopyrightText: 2025-2026 RAprogramm <andrey.rozanov.vl@gmail.com>
// SPDX-License-Identifier: MIT

//! SQL generation level configuration.
//!
//! This module defines [`SqlLevel`], which controls how much database-related
//! code is generated by the Entity macro.

use darling::FromMeta;

/// SQL generation level for the entity.
///
/// Controls how much database-related code is generated by the macro.
/// This allows flexibility between fully automated generation and
/// manual control over SQL queries.
///
/// # Variants
///
/// | Level | Repository Trait | PgPool Impl | Row/Insertable |
/// |-------|-----------------|-------------|----------------|
/// | `Full` | Yes | Yes | Yes |
/// | `Trait` | Yes | No | Yes |
/// | `None` | No | No | No |
///
/// # Examples
///
/// ```rust,ignore
/// // Full generation (default) - everything automated
/// #[entity(table = "users")]
/// #[entity(table = "users", sql = "full")]
///
/// // Trait only - implement SQL yourself for complex queries
/// #[entity(table = "users", sql = "trait")]
///
/// // None - only DTOs, no database layer
/// #[entity(table = "users", sql = "none")]
/// ```
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SqlLevel {
    /// Generate repository trait AND implementation for `PgPool`.
    ///
    /// This is the default level, providing complete CRUD operations
    /// with standard SQL queries. Best for simple entities without
    /// complex query requirements.
    #[default]
    Full,

    /// Generate only the repository trait definition.
    ///
    /// Use this when you need custom SQL queries (joins, CTEs,
    /// full-text search, etc.) but still want the trait interface
    /// and other generated types.
    Trait,

    /// Skip all repository generation.
    ///
    /// Only DTOs are generated. Use this for entities that don't
    /// need database persistence or use a completely different
    /// storage mechanism.
    None
}

impl FromMeta for SqlLevel {
    /// Parse SQL level from string attribute value.
    ///
    /// # Accepted Values
    ///
    /// - `"full"` → [`SqlLevel::Full`]
    /// - `"trait"` → [`SqlLevel::Trait`]
    /// - `"none"` → [`SqlLevel::None`]
    ///
    /// Values are case-insensitive.
    ///
    /// # Errors
    ///
    /// Returns `darling::Error::unknown_value` for unrecognized values.
    fn from_string(value: &str) -> darling::Result<Self> {
        match value.to_lowercase().as_str() {
            "full" => Ok(SqlLevel::Full),
            "trait" => Ok(SqlLevel::Trait),
            "none" => Ok(SqlLevel::None),
            _ => Err(darling::Error::unknown_value(value))
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_is_full() {
        assert_eq!(SqlLevel::default(), SqlLevel::Full);
    }

    #[test]
    fn from_meta_valid() {
        assert_eq!(SqlLevel::from_string("full").unwrap(), SqlLevel::Full);
        assert_eq!(SqlLevel::from_string("FULL").unwrap(), SqlLevel::Full);
        assert_eq!(SqlLevel::from_string("trait").unwrap(), SqlLevel::Trait);
        assert_eq!(SqlLevel::from_string("Trait").unwrap(), SqlLevel::Trait);
        assert_eq!(SqlLevel::from_string("none").unwrap(), SqlLevel::None);
        assert_eq!(SqlLevel::from_string("NONE").unwrap(), SqlLevel::None);
    }

    #[test]
    fn from_meta_invalid() {
        assert!(SqlLevel::from_string("partial").is_err());
        assert!(SqlLevel::from_string("all").is_err());
        assert!(SqlLevel::from_string("").is_err());
    }
}