rjango 0.1.1

A full-stack Rust backend framework inspired by Django
Documentation
use super::mixins::FieldCacheMixin;

/// Whether a generated column is materialized in storage or computed virtually.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GeneratedFieldType {
    Stored,
    Virtual,
}

/// A GeneratedField stores database-level computed column metadata.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GeneratedField {
    pub expression: String,
    pub output_field_type: String,
    pub generated_type: GeneratedFieldType,
    pub db_persist: bool,
}

impl Default for GeneratedField {
    fn default() -> Self {
        Self {
            expression: String::new(),
            output_field_type: "TEXT".to_string(),
            generated_type: GeneratedFieldType::Stored,
            db_persist: true,
        }
    }
}

impl GeneratedField {
    #[must_use]
    pub fn new(expression: impl Into<String>, output_field_type: impl Into<String>) -> Self {
        Self {
            expression: expression.into(),
            output_field_type: output_field_type.into(),
            ..Self::default()
        }
    }

    #[must_use]
    pub fn stored(mut self) -> Self {
        self.generated_type = GeneratedFieldType::Stored;
        self.db_persist = true;
        self
    }

    #[must_use]
    pub fn virtual_column(mut self) -> Self {
        self.generated_type = GeneratedFieldType::Virtual;
        self.db_persist = false;
        self
    }

    #[must_use]
    pub fn expression(&self) -> &str {
        &self.expression
    }

    #[must_use]
    pub fn output_field_type(&self) -> &str {
        &self.output_field_type
    }

    #[must_use]
    pub fn is_stored(&self) -> bool {
        matches!(self.generated_type, GeneratedFieldType::Stored)
    }
}

impl FieldCacheMixin for GeneratedField {
    fn get_cache_name(&self) -> String {
        "generated_field".to_string()
    }

    fn is_cached(&self) -> bool {
        self.db_persist
    }
}

#[cfg(test)]
mod tests {
    use super::{GeneratedField, GeneratedFieldType};

    #[test]
    fn generated_field_defaults_to_stored_column() {
        let field = GeneratedField::default();

        assert_eq!(field.generated_type, GeneratedFieldType::Stored);
        assert!(field.db_persist);
        assert!(field.expression.is_empty());
    }

    #[test]
    fn generated_field_virtual_column_disables_persistence() {
        let field = GeneratedField::new("LOWER(name)", "TEXT").virtual_column();

        assert_eq!(field.generated_type, GeneratedFieldType::Virtual);
        assert!(!field.db_persist);
        assert!(!field.is_stored());
    }

    #[test]
    fn generated_field_exposes_expression() {
        let field = GeneratedField::new("price * quantity", "DECIMAL");

        assert_eq!(field.expression(), "price * quantity");
        assert_eq!(field.output_field_type(), "DECIMAL");
    }
}