package validation import ( "DanceLessonsCoach/pkg/config" "errors" "fmt" "reflect" "strings" "github.com/go-playground/locales/en" en_translations "github.com/go-playground/validator/v10/translations/en" "github.com/go-playground/validator/v10" ut "github.com/go-playground/universal-translator" ) // Validator wraps the go-playground/validator with custom error handling type Validator struct { validate *validator.Validate translator ut.Translator } // NewValidator creates a new validator instance with custom error handling func NewValidator() (*Validator, error) { // Create validator instance validate := validator.New() // Register translations english := en.New() uni := ut.New(english, english) trans, _ := uni.GetTranslator("en") // Register translation for default validator if err := en_translations.RegisterDefaultTranslations(validate, trans); err != nil { return nil, fmt.Errorf("failed to register translations: %w", err) } // Register custom validations if err := registerCustomValidations(validate); err != nil { return nil, fmt.Errorf("failed to register custom validations: %w", err) } return &Validator{ validate: validate, translator: trans, }, nil } // Validate validates a struct and returns validation errors func (v *Validator) Validate(structObj interface{}) error { if structObj == nil { return errors.New("validation: nil object provided") } // Get the type of the struct structType := reflect.TypeOf(structObj) if structType.Kind() != reflect.Struct { return fmt.Errorf("validation: expected struct, got %s", structType.Kind()) } // Perform validation if err := v.validate.Struct(structObj); err != nil { if _, ok := err.(*validator.InvalidValidationError); ok { return fmt.Errorf("validation: invalid validation error: %w", err) } // Convert validation errors to custom format validationErrors := err.(validator.ValidationErrors) return v.formatValidationErrors(validationErrors) } return nil } // formatValidationErrors converts validator.ValidationErrors to a custom error type func (v *Validator) formatValidationErrors(errors validator.ValidationErrors) error { var errorMessages []string for _, err := range errors { field := err.Field() tag := err.Tag() param := err.Param() // Get custom error message message := fmt.Errorf("%s failed validation for '%s'", field, tag).Error() // Add parameter if available if param != "" { message += fmt.Sprintf(" (parameter: %s)", param) } errorMessages = append(errorMessages, message) } return &ValidationError{ Messages: errorMessages, } } // ValidationError represents multiple validation errors type ValidationError struct { Messages []string } func (e *ValidationError) Error() string { return strings.Join(e.Messages, "; ") } // registerCustomValidations registers any custom validation functions func registerCustomValidations(validate *validator.Validate) error { // Add custom validations here as needed // Example: // if err := validate.RegisterValidation("custom_tag", customValidationFunc); err != nil { // return err // } return nil } // GetValidatorFromConfig creates a validator instance based on application config func GetValidatorFromConfig(cfg *config.Config) (*Validator, error) { // For now, config doesn't affect validator creation // But this allows future configuration (e.g., language, strict mode) return NewValidator() }