nautilus-orm-schema 1.0.1

Schema parsing and validation for Nautilus ORM
Documentation
use super::*;

impl SchemaValidator {
    pub(super) fn validate_defaults(&mut self) {
        let models: Vec<_> = self.schema.models().cloned().collect();
        for model in &models {
            for field in &model.fields {
                for attr in &field.attributes {
                    if let FieldAttribute::Default(expr, _) = attr {
                        self.validate_default_value(
                            &field.field_type,
                            expr,
                            &field.name.value,
                            &model.name.value,
                        );
                    }
                }
            }
        }
    }

    pub(super) fn validate_default_value(
        &mut self,
        field_type: &FieldType,
        expr: &Expr,
        field_name: &str,
        model_name: &str,
    ) {
        match expr {
            Expr::Literal(lit) => {
                self.validate_literal_default(field_type, lit, field_name, model_name);
            }
            Expr::Ident(ident) => {
                self.validate_ident_default(field_type, ident, field_name, model_name);
            }
            Expr::FunctionCall { name, args, span } => {
                self.validate_function_default(
                    field_type,
                    &name.value,
                    args,
                    *span,
                    field_name,
                    model_name,
                );
            }
            _ => {
                self.errors.push_back(SchemaError::Validation(
                    format!(
                        "Unsupported default value expression for field '{}' in model '{}'",
                        field_name, model_name
                    ),
                    expr.span(),
                ));
            }
        }
    }

    fn validate_ident_default(
        &mut self,
        field_type: &FieldType,
        ident: &Ident,
        field_name: &str,
        model_name: &str,
    ) {
        let FieldType::UserType(type_name) = field_type else {
            self.push_non_enum_default_identifier_error(ident, field_name, model_name);
            return;
        };
        let Some(enum_decl) = self.schema.enums().find(|e| e.name.value == *type_name) else {
            self.push_non_enum_default_identifier_error(ident, field_name, model_name);
            return;
        };

        if enum_decl
            .variants
            .iter()
            .any(|variant| variant.name.value == ident.value)
        {
            return;
        }

        self.errors.push_back(SchemaError::Validation(
            format!(
                "Enum variant '{}' does not exist in enum '{}' for field '{}' in model '{}'",
                ident.value, type_name, field_name, model_name
            ),
            ident.span,
        ));
    }

    fn push_non_enum_default_identifier_error(
        &mut self,
        ident: &Ident,
        field_name: &str,
        model_name: &str,
    ) {
        self.errors.push_back(SchemaError::Validation(
            format!(
                "Default value for field '{}' in model '{}' uses identifier '{}' but field type is not an enum",
                field_name, model_name, ident.value
            ),
            ident.span,
        ));
    }

    pub(super) fn validate_literal_default(
        &mut self,
        field_type: &FieldType,
        lit: &Literal,
        field_name: &str,
        model_name: &str,
    ) {
        match (field_type, lit) {
            (FieldType::String, Literal::String(_, _)) => {}
            (FieldType::Boolean, Literal::Boolean(_, _)) => {}
            (
                FieldType::Int | FieldType::BigInt | FieldType::Float | FieldType::Decimal { .. },
                Literal::Number(_, _),
            ) => {}
            _ => {
                self.errors.push_back(SchemaError::Validation(
                    format!(
                        "Type mismatch: field '{}' in model '{}' has type {:?} but default value is {:?}",
                        field_name, model_name, field_type, lit
                    ),
                    lit.span(),
                ));
            }
        }
    }

    pub(super) fn validate_function_default(
        &mut self,
        field_type: &FieldType,
        func_name: &str,
        _args: &[Expr],
        span: Span,
        field_name: &str,
        model_name: &str,
    ) {
        match func_name {
            "autoincrement" => {
                if !matches!(field_type, FieldType::Int | FieldType::BigInt) {
                    self.errors.push_back(SchemaError::Validation(
                        format!(
                            "autoincrement() can only be used with Int or BigInt fields, but field '{}' in model '{}' has type {:?}",
                            field_name, model_name, field_type
                        ),
                        span,
                    ));
                }
            }
            "uuid" => {
                if !matches!(field_type, FieldType::Uuid) {
                    self.errors.push_back(SchemaError::Validation(
                        format!(
                            "uuid() can only be used with Uuid fields, but field '{}' in model '{}' has type {:?}",
                            field_name, model_name, field_type
                        ),
                        span,
                    ));
                }
            }
            "now" => {
                if !matches!(field_type, FieldType::DateTime) {
                    self.errors.push_back(SchemaError::Validation(
                        format!(
                            "now() can only be used with DateTime fields, but field '{}' in model '{}' has type {:?}",
                            field_name, model_name, field_type
                        ),
                        span,
                    ));
                }
            }
            "env" => {}
            _ => {
                self.errors.push_back(SchemaError::Validation(
                    format!(
                        "Unknown function '{}' in default value for field '{}' in model '{}'",
                        func_name, field_name, model_name
                    ),
                    span,
                ));
            }
        }
    }
}