valforge

command module
v0.0.3 Latest Latest
Warning

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

Go to latest
Published: Dec 2, 2025 License: MIT Imports: 9 Imported by: 0

README

Valforge

A blazingly fast, struct validation code generator for Go. Valforge generates validation code, eliminating runtime reflection overhead and providing type-safe validation with zero runtime dependencies.

Features

  • Code can generated standalone or using go generate, no reflection or runtime type inspection
  • Validation rules are checked at generation time against your struct field types
  • Easy to add custom validation rules
  • Detailed validation errors with field names and values
  • Built-in JSON error formatting
  • Support for strings, integers, comparisons, email validation, more will be added

Installation

go install github.com/richardbowden/valforge@latest

Quick Start

1. Add validation tags to your structs
package models

type User struct {
    Email    string `json:"email" validate:"required,email"`
    Age      int    `json:"age" validate:"required,gte=18"`
    Password string `json:"password" validate:"required,minlen=8"`
    Confirm  string `json:"confirm" validate:"required,eqfield=Password"`
}
2. Generate validation code
# Generate for a specific file
valforge -file models/user.go

# Or generate for an entire package
valforge -package ./models
3. Use the generated validation
package main

import (
    "fmt"
    "your-module/models"
)

func main() {
    user := models.User{
        Email:    "invalid-email",
        Age:      15,
        Password: "short",
        Confirm:  "different",
    }

    if err := user.Validate(); err != nil {
        fmt.Println(err)
        // Output: User validation failed: email: invalid email format, 
        // age: age must be greater than or equal to 18, 
        // password: password must be at least 8 characters, 
        // confirm: confirm must match Password
    }
}

Available Validation Rules

String Rules
Rule Description Example
required Field cannot be empty validate:"required"
minlen=N Minimum string length validate:"minlen=8"
maxlen=N Maximum string length validate:"maxlen=100"
len=N Exact string length validate:"len=10"
email Valid email format validate:"email"
Numeric Rules
Rule Description Example
required Field cannot be zero validate:"required"
gt=N Greater than validate:"gt=0"
gte=N Greater than or equal validate:"gte=18"
lt=N Less than validate:"lt=100"
lte=N Less than or equal validate:"lte=65"
Cross-Field Rules
Rule Description Example
eqfield=Field Must equal another field validate:"eqfield=Password"
eqfieldsecure=Field Constant-time string comparison validate:"eqfieldsecure=Password"
Combining Rules

Rules can be combined using commas:

type Product struct {
    Name  string `validate:"required,minlen=3,maxlen=100"`
    Price int    `validate:"required,gt=0,lt=1000000"`
}

Configuration Options

# Specify input file
valforge -file path/to/file.go

# Specify input package
valforge -package ./path/to/package

# Specify output file (auto-generated by default)
valforge -output validation.gen.go

# Customize error package name (default: valgen)
valforge -valforge-package myvalidation

# Customize error package path (default: internal/valgen)
valforge -valforge-path internal/myvalidation

# Show version
valforge -version

Generated Code Structure

Valforge generates two types of files:

1. Validation Methods (*_validation.gen.go)

Contains the Validate() method for each struct:

func (v User) Validate() error {
    verr := valgen.NewValidationError("User")
    
    if v.Email == "" {
        verr.AddFieldError("email", "email is required", v.Email)
    }
    // ... more validations
    
    if len(verr.Errors) > 0 {
        return verr
    }
    return nil
}
2. Supporting Package (internal/valgen/)
  • errors.gen.go: Validation error types with JSON support
  • emailvalidation.gen.go: Email validation logic

Error Handling

Validation errors provide multiple ways to inspect failures:

if err := user.Validate(); err != nil {
    validationErr := err.(*valgen.ValidationError)
    
    // Check if a specific field has an error
    if validationErr.HasField("email") {
        fmt.Println("Email is invalid")
    }
    
    // Get all field errors
    for _, fieldErr := range validationErr.Errors {
        fmt.Printf("Field: %s, Message: %s, Value: %v\n", 
            fieldErr.Field, fieldErr.Message, fieldErr.Value)
    }
    
    // Get JSON representation
    jsonBytes, _ := validationErr.JSON()
    fmt.Println(string(jsonBytes))
    // {"struct":"User","errors":[{"field":"email","message":"email is required","value":""}]}
}

Integration with Go Generate

Add a //go:generate directive to your Go files:

//go:generate valforge -file $GOFILE

package models

type User struct {
    Email string `json:"email" validate:"required,email"`
}

Then run:

go generate ./...

Project Structure

your-project/
├── models/
│   ├── user.go                      # Your structs with validation tags
│   └── user_validation.gen.go      # Generated validation methods
├── internal/
│   └── valgen/                      # Generated supporting code
│       ├── errors.gen.go            # Validation error types
│       └── emailvalidation.gen.go  # Email validation logic
└── main.go

Type Safety

Valforge performs compile-time type checking. Invalid configurations will be caught during generation:

// ❌ This will fail at generation time
type User struct {
    Email string `validate:"gt=10"` // Error: gt rule not compatible with string
    Age   string `validate:"email"` // Error: email rule only works with strings
}

Why Code Generation Over Runtime Reflection?

Valforge uses code generation instead of runtime reflection for several important performance and reliability reasons.

Performance Benefits

Runtime validation libraries using reflection have significant performance costs on every validation call. Reflection in Go requires:

  • Dynamic type assertions and conversions
  • Runtime type information lookups
  • Interface allocations for accessing struct fields
  • Method lookups through reflection

Generated code is just plain Go functions with direct field access. The performance is the same as hand-written validation code.

Reflection-based validators create many heap allocations:

  • reflect.Value objects for each field access
  • Interface boxing and unboxing for primitive types
  • Dynamic string allocations for error messages
  • Temporary objects for type conversions

These allocations trigger garbage collection more frequently. In high-throughput services processing thousands of requests per second, this GC pressure causes latency spikes and reduced throughput.

Generated code produces predictable memory access patterns that CPUs can optimize through branch prediction and cache prefetching. Reflection-based code has unpredictable branching and pointer chasing that defeats CPU optimizations.

Production Reliability

With code generation, validation rule errors are caught at build time, not in production. Type mismatches, invalid rule parameters, and missing field references become compiler errors rather than runtime panics.

In typical scenarios, generated validation is 10-50x faster than reflection-based validation. The difference becomes more pronounced as struct complexity increases.

For services handling high request volumes (thousands of validations per second), the cumulative effect of zero allocations and no reflection means:

  • Predictable, low latency with no GC pauses from validation
  • Higher throughput per CPU core
  • Lower memory footprint
  • Better resource utilization and cost efficiency

Code generation makes validation a zero-cost abstraction, suitable for even the most performance-critical microservices.

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

License

[Add your license here]

Version

Current version: Generated at build time

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

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