vix

package
v1.0.41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: MPL-2.0 Imports: 12 Imported by: 0

README

VIX - Type-Safe Validation Library for Go

A modern, type-safe, and expressive validation library for Go that follows clean architecture principles. VIX integrates seamlessly with the ERM centralized error management package for unified error handling and lightweight internationalization using our custom i18n package.

Features

  • Type-Safe Validation: Leverage Go generics for compile-time type safety
  • Expressive API: Fluent, chainable syntax for readable validation rules
  • Lightweight i18n: Uses custom github.com/c3p0-box/utils/i18n package through ERM integration
  • Function Chaining: Readable and maintainable validation rules without struct tags
  • Conditional Validation: Built-in support for conditional validation logic
  • Clean Architecture: Integrates perfectly with onion/clean architecture patterns
  • Unified Error Management: All validation errors are ERM Error instances with automatic localization
  • Centralized Messages: All validation messages managed in ERM's locale system
  • Comprehensive Types: Support for strings, numbers, dates, and custom validations
  • Performance Optimized: Lazy evaluation and efficient validation chains

Architecture Overview

VIX follows a layered architecture for validation and error management:

┌─────────────────────────────────┐
│             VIX                 │  ← Validation Rules & Logic
│        (Type-Safe API)          │
└─────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────┐
│             ERM                 │  ← Error Management & Localization
│     (Error Wrapping & i18n)     │
└─────────────────────────────────┘
                 │
                 ▼
┌─────────────────────────────────┐
│          Custom i18n            │  ← Message Storage & Translation
│    (Lightweight Singleton)      │
└─────────────────────────────────┘
Layer Responsibilities:
  1. VIX Layer - Provides type-safe validation API with fluent syntax

    • Defines validation rules (Required, Email, MinLength, etc.)
    • Manages validation chains and conditional logic
    • Uses message constants that map to localized keys
  2. ERM Layer - Centralizes error management and localization

    • Wraps validation errors with structured metadata
    • Provides localization infrastructure via erm.GetLocalizer()
    • Manages message keys and templates in erm/locale.go
    • Converts validation errors to HTTP-ready responses
  3. Custom i18n Layer - Lightweight message translation

    • Singleton pattern for global message storage
    • Thread-safe translation management
    • Template-based message rendering with Go text templates
    • Minimal dependencies (only Go stdlib + golang.org/x/text)
Message Constants Management:

All validation message keys are centralized in ERM as typed constants:

// Defined in erm/locale.go
const (
    MsgRequired    = "validation.required"
    MsgEmail       = "validation.email" 
    MsgMinLength   = "validation.min_length"
    // ... and many more
)

VIX validators use these constants instead of hardcoded strings:

// In VIX validation logic  
sv.addValidationError(erm.MsgRequired, nil)  // Type-safe message key
Message Flow:
// 1. VIX validation fails
vix.String("", "email").Required() 

// 2. VIX calls ERM with message key
erm.NewValidationError("validation.required", "email", "")

// 3. ERM calls i18n for localized message
i18n.Translate(language.English, "validation.required", 1, map[string]interface{}{
    "field": "email"
})

// 4. i18n returns: "email is required"
Benefits:
  • Centralized Messages: All validation messages defined once in erm/locale.go
  • Type Safety: Compile-time validation of message keys via typed constants
  • Consistent Localization: Same i18n system used across all error types
  • Performance: Message constants avoid runtime string allocations
  • Maintainability: Clear separation between validation logic and presentation
  • DRY Principle: Message constants prevent duplication across validation types
  • IDE Support: Auto-completion and refactoring support for message keys

Installation

go get github.com/c3p0-box/utils/vix

Quick Start

Basic Validation
import "github.com/c3p0-box/utils/vix"

// Single field validation
err := vix.String("john@example.com", "email").
    Required().
    Email().
    MaxLength(100).
    Validate()

if err != nil {
    log.Printf("Email validation failed: %v", err)
}
Multi-Field Validation
// Multiple field validation with structured error output
validator := vix.Is(
    vix.String("", "email").Required().Email(),
    vix.String("123", "password").Required().MinLength(8),
    vix.Int(16, "age").Required().Min(18),
)

if !validator.Valid() {
    // Get structured error map suitable for API responses
    errorMap := validator.ErrMap()
    // Returns: {
    //   "email": ["email is required"],
    //   "password": ["password must be at least 8 characters long"], 
    //   "age": ["age must be at least 18"]
    // }
    
    jsonBytes, _ := validator.ToJSON()
    fmt.Println(string(jsonBytes))
}

Internationalization

VIX uses ERM's integration with our custom lightweight i18n package for localized error messages. All validation messages are centrally managed and automatically localized.

Architecture:

  • Message Definition: All validation messages are defined in erm/locale.go
  • Automatic Localization: VIX validation errors are automatically localized through ERM
  • Default Language: English is the default language, with easy extensibility for additional languages
  • Message Keys: VIX uses standardized message keys (e.g., validation.required, validation.email)

Quick Setup:

// No setup required - localization is automatic!

// All VIX validation errors are automatically localized to English
result := vix.String("", "email").Required().Result()
fmt.Println(result.Error().Error()) // "email is required"

// For different languages, use ERM's localizer
localizer := erm.GetLocalizer(language.Spanish) // If Spanish messages were added

Adding New Languages:

// In your application initialization
import "github.com/c3p0-box/utils/i18n"

// Add Spanish translations for all validation messages
spanishMessages := map[string]*i18n.Translation{
    "validation.required": {Singular: "{{.field}} es requerido", Plural: ""},
    "validation.email":    {Singular: "{{.field}} debe ser un email válido", Plural: ""},
    // ... other validation messages
}

err := i18n.AddTranslations(language.Spanish, spanishMessages)
if err != nil {
    panic(err)
}

String Validation

validator := vix.String("example@email.com", "email")

// Chain validation rules
err := validator.
    Required().              // Field must not be empty
    Email().                // Must be valid email format
    MinLength(5).           // Minimum 5 characters
    MaxLength(100).         // Maximum 100 characters
    Validate()              // Execute validation

// Or get the validation result
result := validator.
    Required().
    Email().
    Result()

if !result.Valid() {
    fmt.Println(result.Error()) // Localized error message
}
Available String Validations
vix.String(value, "fieldName").
    Required().                    // Must not be empty
    Empty().                      // Must be empty
    MinLength(5).                 // Minimum length
    MaxLength(100).               // Maximum length
    ExactLength(10).              // Exact length
    LengthBetween(5, 100).        // Length range
    Email().                      // Valid email format
    URL().                        // Valid URL format
    Numeric().                    // Contains only numbers
    Alpha().                      // Contains only letters
    AlphaNumeric().               // Contains only letters and numbers
    Regex(pattern).               // Matches regex pattern
    In("val1", "val2").          // Value must be in list
    NotIn("val1", "val2").       // Value must not be in list
    EqualTo("expected").         // Value must equal expected string (with optional custom message)
    Contains("substring").        // Must contain substring
    StartsWith("prefix").         // Must start with prefix
    EndsWith("suffix").           // Must end with suffix
    Lowercase().                  // Must be lowercase
    Uppercase().                  // Must be uppercase
    Integer().                    // Must be valid integer
    Float().                      // Must be valid float
    JSON().                       // Must be valid JSON
    Base64().                     // Must be valid base64
    UUID().                       // Must be valid UUID
    Slug()                        // Must be valid slug

Number Validation

// Integer validation
err := vix.Int(25, "age").
    Required().                   // Must not be zero
    Min(18).                     // Minimum value
    Max(100).                    // Maximum value
    Between(18, 65).             // Value range
    Positive().                  // Must be positive
    Even().                      // Must be even number
    Validate()

// Float validation
err = vix.Float64(3.14159, "pi").
    Required().
    Min(0.0).
    Max(10.0).
    Precision(2).                // Maximum 2 decimal places
    Finite().                    // Must be finite (not NaN/Inf)
    Validate()
Available Number Validations
vix.Int(value, "fieldName").       // For int validation
vix.Float64(value, "fieldName").   // For float64 validation

// Common validations for both
    Required().                    // Must not be zero
    Zero().                       // Must be zero
    Min(value).                   // Minimum value
    Max(value).                   // Maximum value
    Between(min, max).            // Value range
    Equal(expected).              // Must equal expected value
    GreaterThan(value).           // Must be greater than
    LessThan(value).              // Must be less than
    Positive().                   // Must be positive
    Negative().                   // Must be negative
    In(val1, val2).              // Must be in list
    NotIn(val1, val2).           // Must not be in list
    EqualTo(expected).           // Must equal expected value (with optional custom message)
    MultipleOf(divisor).          // Must be multiple of divisor

// Integer-specific
    Even().                       // Must be even
    Odd().                        // Must be odd

// Float-specific
    Finite().                     // Must be finite (not NaN/Inf)
    Precision(places).            // Maximum decimal places

Conditional Validation

// Validate phone only if email is empty
phoneValidator := vix.String(phone, "phone").
    When(func() bool { return email == "" }).
    Required().
    Regex(`^\d{10}$`)

// Skip validation based on condition
ageValidator := vix.Int(age, "age").
    Unless(func() bool { return isGuest }).
    Required().
    Min(18)

Advanced Usage

Custom Validation
validator := vix.String("customValue", "field").
    Custom(func(value interface{}, fieldName string) error {
        str := value.(string)
        if !isValidCustomFormat(str) {
            return erm.NewValidationError("validation.custom_format", fieldName, str)
        }
        return nil
    })
EqualTo with Custom Messages
// String validation with default message
err := vix.String("john", "username").
    EqualTo("admin").  // Uses default error message
    Validate()

// String validation with custom message
err = vix.String("john", "username").
    EqualTo("admin", "{{field}} must be exactly '{{expected}}'").
    Validate()

// Number validation with custom message
err = vix.Int(25, "age").
    EqualTo(18, "{{field}} must be exactly {{expected}} years old").
    Validate()

// Works with negation too
err = vix.String("admin", "username").
    Not().EqualTo("root", "{{field}} cannot be '{{expected}}'").
    Validate()
Negation
// Use Not() to negate any validation
validator := vix.String("admin", "username").
    Not().In("admin", "root", "system") // Username must NOT be in this list
Nested Validation
// Validate nested structures
userValidator := vix.Is(
    vix.String(user.Name, "name").Required().MinLength(2),
    vix.String(user.Email, "email").Required().Email(),
)

addressValidator := vix.Is(
    vix.String(user.Address.Street, "address.street").Required(),
    vix.String(user.Address.City, "address.city").Required(),
)

// Combine validators
finalValidator := vix.Is(userValidator, addressValidator)
Array/Slice Validation
// Validate array elements
emails := []string{"user1@example.com", "invalid-email", "user2@example.com"}

validator := vix.V()
for i, email := range emails {
    fieldName := fmt.Sprintf("emails[%d]", i)
    validator = validator.Is(
        vix.String(email, fieldName).Required().Email(),
    )
}

if !validator.Valid() {
    errorMap := validator.ErrMap()
    // Returns: {"emails[1]": ["emails[1] must be a valid email address"]}
}

Error Handling

Single Field Errors
result := vix.String("", "email").Required().Result()

if !result.Valid() {
    err := result.Error()
    fmt.Println(err.Error())     // Localized error message
    
    // Access ERM error details
    if ermErr, ok := err.(erm.Error); ok {
        fmt.Println(ermErr.FieldName())  // "email"
        fmt.Println(ermErr.MessageKey()) // "validation.required"
        fmt.Println(ermErr.Value())      // ""
    }
}
Multi-Field Errors
validator := vix.Is(
    vix.String("", "email").Required().Email(),
    vix.String("123", "password").Required().MinLength(8),
)

if !validator.Valid() {
    // Structured error map
    errorMap := validator.ErrMap()
    // Returns: {
    //   "email": ["email is required"],
    //   "password": ["password must be at least 8 characters long"]
    // }
    
    // JSON output for APIs
    jsonBytes, _ := validator.ToJSON()
    
    // Access all errors through the container error
    if err := validator.Error(); err != nil {
        if ermErr, ok := err.(erm.Error); ok {
            allErrors := ermErr.AllErrors()
            for _, err := range allErrors {
                fmt.Printf("Field: %s, Error: %s\n", 
                    err.(erm.Error).FieldName(), 
                    err.Error())
            }
        }
    }
}
Custom Localization
// Use specific language per request via ERM localizer
localizer := erm.GetLocalizer(language.Spanish)
result := vix.String("", "email").Required().Result()
if !result.Valid() {
    // Get localized error message
    config := erm.LocalizeConfig{
        MessageID: result.Error().(erm.Error).MessageKey(),
        TemplateData: map[string]interface{}{
            "field": result.Error().(erm.Error).FieldName(),
        },
    }
    spanishMsg := localizer.MustLocalize(config)
    fmt.Println(spanishMsg) // "email es requerido" (if Spanish translation available)
}

Clean Architecture Integration

VIX integrates seamlessly with clean architecture patterns through unified ERM error handling:

// Service Layer
func (s *userService) CreateUser(req CreateUserRequest) (*User, error) {
    validator := vix.Is(
        vix.String(req.Email, "email").Required().Email(),
        vix.String(req.Password, "password").Required().MinLength(8),
        vix.Int(req.Age, "age").Required().Min(18),
    )
    
    if !validator.Valid() {
        return nil, erm.BadRequest("Validation failed", validator.Error())
    }
    
    // Business logic...
    return s.userRepo.Create(user)
}

// Handler Layer - ERM provides structured error responses
func (h *userHandler) CreateUser(w http.ResponseWriter, r *http.Request) {
    user, err := h.userService.CreateUser(req)
    if err != nil {
        // ERM automatically formats validation errors for APIs
        erm.WriteJSONError(w, err) // See ERM documentation
        return
    }
    
    json.NewEncoder(w).Encode(user)
}

API Reference

Core Functions
  • String(value, fieldName string) *StringValidator - Create string validator
  • Int(value int, fieldName string) *NumberValidator[int] - Create int validator
  • Float64(value float64, fieldName string) *NumberValidator[float64] - Create float validator
  • Is(validators ...Validator) *ValidationOrchestrator - Multi-field validation
  • V() *ValidationOrchestrator - Create validation orchestrator
Validator Interface
type Validator interface {
    Valid() bool
    Error() error
    AllErrors() []error
    Result() *ValidationResult
}
ValidationResult
type ValidationResult struct {
    Value      interface{}
    FieldName  string
    IsValid    bool
}

func (vr *ValidationResult) Valid() bool
func (vr *ValidationResult) Error() error
func (vr *ValidationResult) AllErrors() []error
func (vr *ValidationResult) ErrMap() map[string][]string
func (vr *ValidationResult) ToJSON() ([]byte, error)
ValidationOrchestrator
// ValidationOrchestrator manages multiple field validations
func (vo *ValidationOrchestrator) Valid() bool
func (vo *ValidationOrchestrator) Error() error  // Returns single erm.Error containing all validation errors
func (vo *ValidationOrchestrator) ErrMap() map[string][]string
func (vo *ValidationOrchestrator) ToJSON() ([]byte, error)

Note: ValidationOrchestrator.Error() returns a single erm.Error that contains all validation errors as child errors. Use err.(erm.Error).AllErrors() to access individual errors if needed.

Migration from External i18n Dependencies

VIX now uses a custom lightweight i18n package instead of external dependencies:

From go-i18n/v2:
// OLD: External go-i18n/v2 dependency
import "github.com/nicksnyder/go-i18n/v2/i18n"

// NEW: Custom lightweight i18n package
import "github.com/c3p0-box/utils/i18n"
From WithLocale() Method:
// OLD: WithLocale() method (removed)
validator.WithLocale(language.Spanish).Validate()

// NEW: Use ERM localizer for custom languages
localizer := erm.GetLocalizer(language.Spanish)
// Errors are automatically localized when added to i18n
Benefits of Migration:
  • Reduced Dependencies: No external i18n dependencies
  • Better Performance: Lightweight singleton pattern
  • Centralized Management: All messages in one place (erm/locale.go)
  • Type Safety: Compile-time validation of message keys

Best Practices

  1. Use localized validation: Get localizers using erm.GetLocalizer(language.Tag) for different languages
  2. Use structured validation: Prefer vix.Is() for multi-field validation
  3. Validate at boundaries: Validate input at service layer boundaries
  4. Consistent field names: Use consistent field naming across your application
  5. Custom validators: Create reusable custom validators for domain-specific rules
  6. Error collection: Use error collection for batch validation scenarios
  7. Conditional validation: Use When() and Unless() for complex business rules

Performance Considerations

  • Lazy evaluation: Validation chains are only executed when needed
  • Memory efficient: Validators reuse internal structures where possible
  • Minimal allocations: Careful design to minimize memory allocations
  • Early termination: Validation stops at first failure for single-field validation
  • Batch processing: Multi-field validation collects all errors efficiently

Testing

go test -v ./vix
go test -bench=. ./vix

License

This package is part of the c3p0-box/utils collection.

Documentation

Overview

Package vix provides a type-safe, expressive, and extensible validation library for Go. It follows clean architecture principles and integrates seamlessly with the ERM centralized error management package for unified error handling and error collection with custom lightweight i18n support.

Unlike tag-based validation libraries, this package uses function chaining to create readable and maintainable validation rules. The package supports type-safe validation with Go generics, internationalization using the custom i18n package through ERM integration, conditional validation, and comprehensive error reporting suitable for modern APIs.

All validation errors are now unified under the erm.Error interface, providing consistent error handling across application and validation layers with on-demand localization support.

Message Constants Architecture

VIX uses centralized message constants defined in the ERM package (erm.Msg* constants) to ensure type safety and avoid hardcoded strings. All validation messages are managed by ERM's localization system, providing consistent internationalization across the entire validation framework.

Example usage:

err := vix.String("", "email").Required().Validate()
// Uses erm.MsgRequired internally for localized error messages

Quick Start

Basic validation example:

import "github.com/c3p0-box/utils/vix"

// Single field validation
err := vix.String("john@example.com", "email").
	Required().
	Email().
	MaxLength(100).
	Validate()

if err != nil {
	log.Printf("Email validation failed: %v", err)
}

Multi-Field Validation

The package excels at validating multiple fields with structured error output:

val := vix.Is(
	vix.String("", "email").Required().Email(),
	vix.Int(16, "age").Required().Min(18),
)

if !val.Valid() {
	errorMap := val.ErrMap()
	jsonBytes, _ := val.ToJSON()
	// Returns structured error map suitable for API responses
}

Conditional Validation

Validators support conditional logic:

err := vix.String(phone, "phone").
	When(func() bool { return email == "" }).
	Required().
	Validate()

Integration with Clean Architecture

The package integrates seamlessly with onion/clean architecture patterns using the unified erm error management system:

func (s *UserService) CreateUser(user User) error {
	if err := vix.String(user.Email, "email").Required().Email().Validate(); err != nil {
		return erm.BadRequest("Invalid email", err)
	}
	// Business logic continues...
	return nil
}

Internationalization

Error messages are localized using the custom i18n package (github.com/c3p0-box/utils/i18n) through ERM integration. Set up a localizer in your application initialization:

// Get localizers for different languages
englishLocalizer := erm.GetLocalizer(language.English)
spanishLocalizer := erm.GetLocalizer(language.Spanish)

All validation errors will then be automatically localized when converted to strings.

Index

Constants

This section is empty.

Variables

View Source
var (
	EmailRegex        = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
	URLRegex          = regexp.MustCompile(`^https?://[^\s/$.?#].[^\s]*$`)
	NumericRegex      = regexp.MustCompile(`^[0-9]+$`)
	AlphaRegex        = regexp.MustCompile(`^[a-zA-Z]+$`)
	AlphaNumericRegex = regexp.MustCompile(`^[a-zA-Z0-9]+$`)
)

Common validation patterns

Functions

This section is empty.

Types

type BaseValidator

type BaseValidator struct {
	// contains filtered or unexported fields
}

BaseValidator provides common functionality for all validators.

func NewBaseValidator

func NewBaseValidator(value interface{}, fieldName string) *BaseValidator

NewBaseValidator creates a new BaseValidator.

func (*BaseValidator) Custom

func (bv *BaseValidator) Custom(fn func(value interface{}, fieldName string) error) *BaseValidator

Custom validates using a custom validation function. The function receives both the value being validated and the field name, allowing for more contextual error messages.

Example:

err := vix.String("test", "username").
	Custom(func(value interface{}, fieldName string) error {
		str := value.(string)
		if strings.Contains(str, "admin") {
			return erm.NewValidationError("{{field}} cannot contain 'admin'", fieldName, value)
		}
		return nil
	}).
	Validate()

func (*BaseValidator) Not

func (bv *BaseValidator) Not() *BaseValidator

Not negates the next validation rule.

func (*BaseValidator) Result

func (bv *BaseValidator) Result() *ValidationResult

Result returns the full validation result.

func (*BaseValidator) Unless

func (bv *BaseValidator) Unless(condition func() bool) *BaseValidator

Unless adds a condition that must be false for validation to run.

func (*BaseValidator) Validate

func (bv *BaseValidator) Validate() error

Validate returns the validation result.

func (*BaseValidator) When

func (bv *BaseValidator) When(condition func() bool) *BaseValidator

When adds a condition that must be true for validation to run.

type Number

type Number interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64 |
		~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 |
		~float32 | ~float64
}

Number defines a type constraint for all numeric types

type NumberValidator

type NumberValidator[T Number] struct {
	*BaseValidator
	// contains filtered or unexported fields
}

NumberValidator provides validation rules for numeric values. It supports method chaining for readable and maintainable validation.

func Float32

func Float32(value float32, fieldName string) *NumberValidator[float32]

Float32 creates a NumberValidator for float32 values.

func Float64

func Float64(value float64, fieldName string) *NumberValidator[float64]

Float64 creates a NumberValidator for float64 values.

func Int

func Int(value int, fieldName string) *NumberValidator[int]

Int creates a NumberValidator for int values.

func Int8

func Int8(value int8, fieldName string) *NumberValidator[int8]

Int8 creates a NumberValidator for int8 values.

func Int16

func Int16(value int16, fieldName string) *NumberValidator[int16]

Int16 creates a NumberValidator for int16 values.

func Int32

func Int32(value int32, fieldName string) *NumberValidator[int32]

Int32 creates a NumberValidator for int32 values.

func Int64

func Int64(value int64, fieldName string) *NumberValidator[int64]

Int64 creates a NumberValidator for int64 values.

func Numeric

func Numeric[T Number](value T, fieldName string) *NumberValidator[T]

Numeric creates a new NumberValidator for the given value and field name. This function uses Go generics to support all numeric types.

Example:

err := validator.Numeric(42, "age").
	Min(18).
	Max(100).
	Validate()

func Uint

func Uint(value uint, fieldName string) *NumberValidator[uint]

Uint creates a NumberValidator for uint values.

func Uint8

func Uint8(value uint8, fieldName string) *NumberValidator[uint8]

Uint8 creates a NumberValidator for uint8 values.

func Uint16

func Uint16(value uint16, fieldName string) *NumberValidator[uint16]

Uint16 creates a NumberValidator for uint16 values.

func Uint32

func Uint32(value uint32, fieldName string) *NumberValidator[uint32]

Uint32 creates a NumberValidator for uint32 values.

func Uint64

func Uint64(value uint64, fieldName string) *NumberValidator[uint64]

Uint64 creates a NumberValidator for uint64 values.

func (*NumberValidator[T]) Between

func (nv *NumberValidator[T]) Between(min, max T) *NumberValidator[T]

Between validates that the number is between min and max (inclusive).

func (*NumberValidator[T]) Custom

func (nv *NumberValidator[T]) Custom(fn func(value interface{}, fieldName string) error) *NumberValidator[T]

Custom validates using a custom validation function. The function receives both the numeric value being validated and the field name, allowing for more contextual error messages.

Example:

err := vix.Int(13, "age").
	Custom(func(value interface{}, fieldName string) error {
		age := value.(int)
		if age == 13 {
			return erm.NewValidationError("{{field}} cannot be unlucky number 13", fieldName, value)
		}
		return nil
	}).
	Validate()

func (*NumberValidator[T]) Equal

func (nv *NumberValidator[T]) Equal(expected T) *NumberValidator[T]

Equal validates that the number equals the specified value.

func (*NumberValidator[T]) EqualTo

func (nv *NumberValidator[T]) EqualTo(expected T, msgTemplate ...string) *NumberValidator[T]

EqualTo validates that the number equals the specified value. Optionally accepts a custom error message template as the second parameter. If no custom message is provided, uses the default localized message.

Example:

// With default message
err := vix.Int(25, "age").EqualTo(18).Validate()

// With custom message template
err = vix.Int(25, "age").
	EqualTo(18, "{{field}} must be exactly {{expected}} years old").
	Validate()

// Works with negation and different numeric types
err = vix.Float64(3.14, "pi").
	Not().EqualTo(2.71).
	Validate()

func (*NumberValidator[T]) Even

func (nv *NumberValidator[T]) Even() *NumberValidator[T]

Even validates that the number is even.

func (*NumberValidator[T]) Finite

func (nv *NumberValidator[T]) Finite() *NumberValidator[T]

Finite validates that the number is finite (for floating-point numbers).

func (*NumberValidator[T]) GreaterThan

func (nv *NumberValidator[T]) GreaterThan(value T) *NumberValidator[T]

GreaterThan validates that the number is greater than the specified value.

func (*NumberValidator[T]) In

func (nv *NumberValidator[T]) In(values ...T) *NumberValidator[T]

In validates that the number is one of the specified values.

func (*NumberValidator[T]) LessThan

func (nv *NumberValidator[T]) LessThan(value T) *NumberValidator[T]

LessThan validates that the number is less than the specified value.

func (*NumberValidator[T]) Max

func (nv *NumberValidator[T]) Max(max T) *NumberValidator[T]

Max validates that the number is less than or equal to the maximum value.

func (*NumberValidator[T]) Min

func (nv *NumberValidator[T]) Min(min T) *NumberValidator[T]

Min validates that the number is greater than or equal to the minimum value.

func (*NumberValidator[T]) MultipleOf

func (nv *NumberValidator[T]) MultipleOf(divisor T) *NumberValidator[T]

MultipleOf validates that the number is a multiple of the specified value.

func (*NumberValidator[T]) Negative

func (nv *NumberValidator[T]) Negative() *NumberValidator[T]

Negative validates that the number is negative (less than zero).

func (*NumberValidator[T]) Not

func (nv *NumberValidator[T]) Not() *NumberValidator[T]

Not negates the next validation rule.

func (*NumberValidator[T]) NotIn

func (nv *NumberValidator[T]) NotIn(values ...T) *NumberValidator[T]

NotIn validates that the number is not one of the specified values.

func (*NumberValidator[T]) Odd

func (nv *NumberValidator[T]) Odd() *NumberValidator[T]

Odd validates that the number is odd.

func (*NumberValidator[T]) Positive

func (nv *NumberValidator[T]) Positive() *NumberValidator[T]

Positive validates that the number is positive (greater than zero).

func (*NumberValidator[T]) Precision

func (nv *NumberValidator[T]) Precision(places int) *NumberValidator[T]

Precision validates that a float has at most the specified number of decimal places.

func (*NumberValidator[T]) Required

func (nv *NumberValidator[T]) Required() *NumberValidator[T]

Required validates that the number is not zero.

func (*NumberValidator[T]) Unless

func (nv *NumberValidator[T]) Unless(condition func() bool) *NumberValidator[T]

Unless adds a condition that must be false for validation to run.

func (*NumberValidator[T]) When

func (nv *NumberValidator[T]) When(condition func() bool) *NumberValidator[T]

When adds a condition that must be true for validation to run.

func (*NumberValidator[T]) Zero

func (nv *NumberValidator[T]) Zero() *NumberValidator[T]

Zero validates that the number is zero.

type StringValidator

type StringValidator struct {
	*BaseValidator
}

StringValidator provides validation rules for string values. It supports method chaining for readable and maintainable validation.

func String

func String(value string, fieldName string) *StringValidator

String creates a new StringValidator for the given value and field name.

Example:

err := validator.String("john@example.com", "email").
	Required().
	Email().
	MaxLength(100).
	Validate()

func (*StringValidator) Alpha

func (sv *StringValidator) Alpha() *StringValidator

Alpha validates that the string contains only alphabetic characters.

func (*StringValidator) AlphaNumeric

func (sv *StringValidator) AlphaNumeric() *StringValidator

AlphaNumeric validates that the string contains only alphanumeric characters.

func (*StringValidator) Base64

func (sv *StringValidator) Base64() *StringValidator

Base64 validates that the string is valid base64.

func (*StringValidator) Contains

func (sv *StringValidator) Contains(substring string) *StringValidator

Contains validates that the string contains the specified substring.

func (*StringValidator) Custom

func (sv *StringValidator) Custom(fn func(value interface{}, fieldName string) error) *StringValidator

Custom validates using a custom validation function. The function receives both the string value being validated and the field name, allowing for more contextual error messages.

Example:

err := vix.String("test@admin.com", "email").
	Custom(func(value interface{}, fieldName string) error {
		str := value.(string)
		if strings.HasSuffix(str, "@admin.com") {
			return &ValidationError{Message: fieldName + " cannot use admin domain"}
		}
		return nil
	}).
	Validate()

func (*StringValidator) Email

func (sv *StringValidator) Email() *StringValidator

Email validates that the string is a valid email address format.

func (*StringValidator) Empty

func (sv *StringValidator) Empty() *StringValidator

Empty validates that the string is empty (exactly empty, not just whitespace).

func (*StringValidator) EndsWith

func (sv *StringValidator) EndsWith(suffix string) *StringValidator

EndsWith validates that the string ends with the specified suffix.

func (*StringValidator) EqualTo

func (sv *StringValidator) EqualTo(other string, msgTemplate ...string) *StringValidator

EqualTo validates that the string equals the specified value. Optionally accepts a custom error message template as the second parameter. If no custom message is provided, uses the default localized message.

Example:

// With default message
err := vix.String("john", "username").EqualTo("admin").Validate()

// With custom message template
err = vix.String("john", "username").
	EqualTo("admin", "{{field}} must be exactly '{{expected}}'").
	Validate()

// Works with negation
err = vix.String("admin", "username").
	Not().EqualTo("root").
	Validate()

func (*StringValidator) ExactLength

func (sv *StringValidator) ExactLength(length int) *StringValidator

ExactLength validates that the string has exactly the specified number of characters.

func (*StringValidator) Float

func (sv *StringValidator) Float() *StringValidator

Float validates that the string represents a valid floating-point number.

func (*StringValidator) In

func (sv *StringValidator) In(values ...string) *StringValidator

In validates that the string is one of the specified values.

func (*StringValidator) Integer

func (sv *StringValidator) Integer() *StringValidator

Integer validates that the string represents a valid integer.

func (*StringValidator) JSON

func (sv *StringValidator) JSON() *StringValidator

JSON validates that the string is valid JSON.

func (*StringValidator) LengthBetween

func (sv *StringValidator) LengthBetween(min, max int) *StringValidator

LengthBetween validates that the string length is between min and max (inclusive).

func (*StringValidator) Lowercase

func (sv *StringValidator) Lowercase() *StringValidator

Lowercase validates that the string is in lowercase.

func (*StringValidator) MaxLength

func (sv *StringValidator) MaxLength(max int) *StringValidator

MaxLength validates that the string has at most the specified number of characters.

func (*StringValidator) MinLength

func (sv *StringValidator) MinLength(min int) *StringValidator

MinLength validates that the string has at least the specified number of characters.

func (*StringValidator) Not

func (sv *StringValidator) Not() *StringValidator

Not negates the next validation rule.

func (*StringValidator) NotIn

func (sv *StringValidator) NotIn(values ...string) *StringValidator

NotIn validates that the string is not one of the specified values.

func (*StringValidator) Numeric

func (sv *StringValidator) Numeric() *StringValidator

Numeric validates that the string contains only numeric characters.

func (*StringValidator) Regex

func (sv *StringValidator) Regex(pattern *regexp.Regexp) *StringValidator

Regex validates that the string matches the given regular expression.

func (*StringValidator) Required

func (sv *StringValidator) Required() *StringValidator

Required validates that the string is not empty (after trimming whitespace).

func (*StringValidator) Slug

func (sv *StringValidator) Slug() *StringValidator

Slug validates that the string is a valid URL slug.

func (*StringValidator) StartsWith

func (sv *StringValidator) StartsWith(prefix string) *StringValidator

StartsWith validates that the string starts with the specified prefix.

func (*StringValidator) URL

func (sv *StringValidator) URL() *StringValidator

URL validates that the string is a valid URL format.

func (*StringValidator) UUID

func (sv *StringValidator) UUID() *StringValidator

UUID validates that the string is a valid UUID.

func (*StringValidator) Unless

func (sv *StringValidator) Unless(condition func() bool) *StringValidator

Unless adds a condition that must be false for validation to run.

func (*StringValidator) Uppercase

func (sv *StringValidator) Uppercase() *StringValidator

Uppercase validates that the string is in uppercase.

func (*StringValidator) When

func (sv *StringValidator) When(condition func() bool) *StringValidator

When adds a condition that must be true for validation to run.

type ValidationOrchestrator

type ValidationOrchestrator struct {
	// contains filtered or unexported fields
}

ValidationOrchestrator manages multiple validators and provides a unified interface for validating multiple fields and creating comprehensive error reports. It supports namespace organization for nested structures and arrays.

The orchestrator collects validation results from multiple validators and provides methods to check validity, retrieve errors, and generate structured output suitable for API responses. All error messages are automatically localized through the ERM package's i18n system.

The orchestrator uses an erm.Error container internally to collect and manage all validation errors, leveraging ERM's error collection capabilities for efficient error handling and reporting.

func In

func In(namespace string, orchestrator *ValidationOrchestrator) *ValidationOrchestrator

In creates a new ValidationOrchestrator with a single namespaced validation orchestrator. This is a convenience function that allows you to write vix.In(...) instead of vix.V().In(...).

Example:

val := vix.In("address", vix.Is(
	vix.String(address.Street, "street").Required(),
	vix.String(address.City, "city").Required(),
))

func InRow

func InRow(namespace string, index int, orchestrator *ValidationOrchestrator) *ValidationOrchestrator

InRow creates a new ValidationOrchestrator with a single indexed namespaced validation orchestrator. This is a convenience function that allows you to write vix.InRow(...) instead of vix.V().InRow(...).

Example:

val := vix.InRow("addresses", 0, vix.Is(
	vix.String(address.Street, "street").Required(),
	vix.String(address.City, "city").Required(),
))

func Is

func Is(validators ...Validator) *ValidationOrchestrator

Is creates a new ValidationOrchestrator and adds the given validators to it. This is a convenience function that allows you to write vix.Is(...) instead of vix.V().Is(...).

Example:

val := vix.Is(
	vix.String("test@example.com", "email").Required().Email(),
	vix.Int(25, "age").Required().Min(18),
)

if !val.Valid() {
	errorMap := val.ErrMap()
	// handle errors
}

func NewValidationOrchestrator

func NewValidationOrchestrator() *ValidationOrchestrator

NewValidationOrchestrator creates a new ValidationOrchestrator.

func V

V creates a new ValidationOrchestrator. This is a shorthand function for convenience.

func (*ValidationOrchestrator) ErrMap

func (vo *ValidationOrchestrator) ErrMap() map[string][]string

ErrMap returns a map of field names to error messages using the default localizer. Now leverages ERM's localized error map functionality while preserving the namespaced field names managed by the orchestrator.

func (*ValidationOrchestrator) Error

func (vo *ValidationOrchestrator) Error() error

Error returns a single erm.Error containing all validation errors as children.

func (*ValidationOrchestrator) FieldNames

func (vo *ValidationOrchestrator) FieldNames() []string

FieldNames returns all field names that have been validated.

func (*ValidationOrchestrator) GetFieldResult

func (vo *ValidationOrchestrator) GetFieldResult(fieldName string) *ValidationResult

GetFieldResult returns the validation result for a specific field.

func (*ValidationOrchestrator) In

In adds validations within a namespace. The namespace is prefixed to field names.

func (*ValidationOrchestrator) InRow

func (vo *ValidationOrchestrator) InRow(namespace string, index int, orchestrator *ValidationOrchestrator) *ValidationOrchestrator

InRow adds validations within an indexed namespace. The namespace and index are prefixed to field names.

func (*ValidationOrchestrator) Is

Is adds multiple validators to the orchestrator.

func (*ValidationOrchestrator) IsValid

func (vo *ValidationOrchestrator) IsValid(fieldName string) bool

IsValid returns true if the specific field validation passed.

func (*ValidationOrchestrator) LocalizedErrMap

func (vo *ValidationOrchestrator) LocalizedErrMap(tag language.Tag) map[string][]string

LocalizedErrMap returns a map of field names to localized error messages for the specified language. This provides full internationalization support while preserving the orchestrator's namespaced field structure.

func (*ValidationOrchestrator) String

func (vo *ValidationOrchestrator) String() string

String returns a string representation of the validation state.

func (*ValidationOrchestrator) ToJSON

func (vo *ValidationOrchestrator) ToJSON() ([]byte, error)

ToJSON returns the error map as JSON bytes.

func (*ValidationOrchestrator) Valid

func (vo *ValidationOrchestrator) Valid() bool

Valid returns true if all validations passed.

type ValidationResult

type ValidationResult struct {
	Value     interface{}
	FieldName string

	IsValid bool
	// contains filtered or unexported fields
}

ValidationResult holds the result of a validation operation. It contains the original value, field name, and collects validation errors as a slice of erm.Error instances for unified error handling. Internationalization is handled automatically through the erm package. All validation errors use HTTP 400 Bad Request status code.

func NewValidationResult

func NewValidationResult(value interface{}, fieldName string) *ValidationResult

NewValidationResult creates a new ValidationResult with the given value and field name.

func (*ValidationResult) AddError

func (vr *ValidationResult) AddError(err error) *ValidationResult

AddError adds a validation error to the result.

func (*ValidationResult) AllErrors

func (vr *ValidationResult) AllErrors() []erm.Error

AllErrors returns all validation errors.

func (*ValidationResult) ErrMap

func (vr *ValidationResult) ErrMap() map[string][]string

ErrMap returns a map of field names to error messages. Returns nil if validation passed, otherwise returns the structured error map.

func (*ValidationResult) Error

func (vr *ValidationResult) Error() error

Error returns the validation error container, or nil if validation passed.

func (*ValidationResult) Valid

func (vr *ValidationResult) Valid() bool

Valid returns true if no validation errors occurred.

type Validator

type Validator interface {
	Validate() error
	Result() *ValidationResult
}

Validator defines the interface that all validation rules must implement. It provides a single Validate method that returns an error if validation fails.

type ValidatorChain

type ValidatorChain interface {
	// Not negates the next validation rule
	Not() ValidatorChain

	// When adds a condition that must be true for validation to run
	When(condition func() bool) ValidatorChain

	// Unless adds a condition that must be false for validation to run
	Unless(condition func() bool) ValidatorChain

	// Custom validates using a custom validation function
	Custom(fn func(value interface{}, fieldName string) error) ValidatorChain
}

ValidatorChain defines the interface for validator chaining operations. This interface eliminates code duplication by providing common methods that can be shared across different validator types.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL