- Add ADR-0012 documenting the decision to format only staged Go files - Update ADR README.md with new entry - Document rationale, alternatives, and verification results - Include future considerations for monitoring and CI/CD integration Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
124 lines
3.4 KiB
Go
124 lines
3.4 KiB
Go
package validation
|
|
|
|
import (
|
|
"DanceLessonsCoach/pkg/config"
|
|
"errors"
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/go-playground/locales/en"
|
|
ut "github.com/go-playground/universal-translator"
|
|
"github.com/go-playground/validator/v10"
|
|
en_translations "github.com/go-playground/validator/v10/translations/en"
|
|
)
|
|
|
|
// 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()
|
|
}
|