Files
cv-site/internal/validation/errors.go
T
juanatsap 6c7595b041 feat: add tag-based validation system with reflection caching
Implement a declarative struct tag validation system for Go:

- Add validator.go with sync.Map caching for reflection metadata
- Add rules.go with 11 built-in validation rules (required, email,
  pattern, honeypot, timing, etc.)
- Add errors.go with FieldError and ValidationErrors types
- Update ContactFormRequest with validate tags
- Add ValidateContactFormV2() using the new tag-based validator

Rules implemented:
- required/optional: field presence validation
- trim/sanitize: automatic value transformations
- min/max: UTF-8 aware length validation
- email: RFC 5322 email format validation
- pattern: predefined regex patterns (name, subject, company)
- no_injection: email header injection prevention
- honeypot: bot trap (must be empty)
- timing: timestamp validation for bot detection

Documentation:
- docs/go-validation-system.md: complete validation guide
- docs/go-template-system.md: template manager documentation
- docs/go-routes-api.md: routes and API reference
- docs/README.md: documentation index
2025-12-06 15:20:45 +00:00

66 lines
1.6 KiB
Go

package validation
import "strings"
// FieldError represents a single field validation error
type FieldError struct {
Field string `json:"field"` // Field name
Tag string `json:"tag"` // Validation tag that failed
Param string `json:"param,omitempty"` // Optional parameter (e.g., "100" for "max=100")
Message string `json:"message"` // Human-readable error message
}
// Error implements the error interface
func (e FieldError) Error() string {
if e.Param != "" {
return e.Field + ": " + e.Message + " (" + e.Tag + "=" + e.Param + ")"
}
return e.Field + ": " + e.Message
}
// ValidationErrors represents multiple field validation errors
type ValidationErrors []FieldError
// Error implements the error interface
func (ve ValidationErrors) Error() string {
if len(ve) == 0 {
return ""
}
var sb strings.Builder
sb.WriteString("validation failed: ")
for i, err := range ve {
if i > 0 {
sb.WriteString(", ")
}
sb.WriteString(err.Error())
}
return sb.String()
}
// HasErrors returns true if there are any validation errors
func (ve ValidationErrors) HasErrors() bool {
return len(ve) > 0
}
// GetFieldError returns the first error for a specific field, or nil if none
func (ve ValidationErrors) GetFieldError(field string) *FieldError {
for _, err := range ve {
if err.Field == field {
return &err
}
}
return nil
}
// GetFieldErrors returns all errors for a specific field
func (ve ValidationErrors) GetFieldErrors(field string) []FieldError {
var errors []FieldError
for _, err := range ve {
if err.Field == field {
errors = append(errors, err)
}
}
return errors
}