Files
dance-lessons-coach/pkg/validation/validator.go
Gabriel Radureau 9336178d73 📝 docs: add ADR for staged-only Git hooks formatting
- 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>
2026-04-04 21:36:57 +02:00

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()
}