valet

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Dec 5, 2025 License: MIT Imports: 20 Imported by: 0

README

Valet

Go Reference Go Report Card Coverage Security

A high-performance, Zod-inspired validation library for Go with a fluent builder API. Works with dynamic map data (map[string]any) - perfect for validating JSON payloads, API requests, and configuration data without requiring pre-defined structs.

Table of Contents

Features

  • Fluent Builder API - Zod-like chainable validation rules
  • Zero Dependencies - Only uses Go standard library
  • High Performance - Optimized with sync.Pool, regex caching, and parallel execution
  • Type-Safe Generics - Numeric validators use Go generics
  • Nested Validation - Validate deeply nested objects and arrays
  • Conditional Validation - RequiredIf and RequiredUnless rules
  • Custom Validators - Add your own validation logic
  • Custom Error Messages - Inline messages with dynamic template functions
  • Integrated DB Validation - Exists/Unique checks with batched queries (N+1 prevention)
  • Multiple DB Adapters - Support for database/sql, GORM, sqlx, Bun, and custom implementations
  • Parallel DB Queries - Multi-table checks execute concurrently
  • Context Support - Full context.Context support for cancellation

Requirements

  • Go 1.21+ (uses generics)
  • No external dependencies

Installation

go get github.com/ezartsh/valet

Quick Start

package main

import (
    "encoding/json"
    "fmt"
    "github.com/ezartsh/valet"
)

func main() {
    // Parse JSON into map
    jsonData := []byte(`{
        "name": "John Doe",
        "email": "john@example.com",
        "age": 25
    }`)
    
    var data map[string]any
    json.Unmarshal(jsonData, &data)
    
    // Define validation schema
    schema := valet.Schema{
        "name":  valet.String().Required().Min(2).Max(100),
        "email": valet.String().Required().Email(),
        "age":   valet.Num[float64]().Required().Min(18).Max(120),
    }
    
    // Validate
    if err := valet.Validate(data, schema); err != nil {
        fmt.Println("Validation failed:", err.Errors)
    } else {
        fmt.Println("Validation passed!")
    }
}

Available Validation Rules

String Rules
Rule Description
Required() Field must be present and non-empty
RequiredIf(fn) Required if condition is met
RequiredUnless(fn) Required unless condition is met
Min(n) Minimum string length
Max(n) Maximum string length
Length(n) Exact string length
Email() Valid email format
URL() Valid URL format
UUID() Valid UUID format
ULID() Valid ULID format
IP() Valid IP address (IPv4 or IPv6)
IPv4() Valid IPv4 address
IPv6() Valid IPv6 address
MAC() Valid MAC address
JSON() Valid JSON string
Base64() Valid Base64 encoded string
HexColor() Valid hex color code
Alpha() Contains only letters
AlphaNumeric() Contains only letters and numbers
AlphaDash() Letters, numbers, dashes, underscores
ASCII() Contains only ASCII characters
Digits(n) Contains only digits with exact length
Regex(pattern) Must match regex pattern
NotRegex(pattern) Must NOT match regex pattern
In(values...) Must be one of specified values
NotIn(values...) Must NOT be one of specified values
StartsWith(prefix) Must start with prefix
EndsWith(suffix) Must end with suffix
DoesntStartWith(prefixes...) Must NOT start with any of prefixes
DoesntEndWith(suffixes...) Must NOT end with any of suffixes
Contains(substr) Must contain substring
Includes(substrs...) Must contain all substrings
SameAs(field) Must equal another field's value
DifferentFrom(field) Must differ from another field's value
Trim() Trim whitespace before validation
Lowercase() Convert to lowercase before validation
Uppercase() Convert to uppercase before validation
Transform(fn) Apply custom transformation
Exists(table, column) Value must exist in database
Unique(table, column, ignore) Value must be unique in database
Custom(fn) Custom validation function
Nullable() Allow null values
Default(value) Set default value if nil
String Examples
// Basic validations
valet.String().Required()
valet.String().Min(3).Max(100)
valet.String().Email()
valet.String().UUID()

// With custom messages
valet.String().Required("Name is required")
valet.String().Email("Please enter a valid email")

// Advanced validations
valet.String().Regex(`^[A-Z]{2}\d{4}$`)
valet.String().In("draft", "published", "archived")
valet.String().StartsWith("PRE_")

// Password confirmation
valet.String().SameAs("password")

// Transformations
valet.String().Trim().Lowercase().Email()

// Database checks
valet.String().Unique("users", "email", nil)
valet.String().Exists("categories", "slug")
Number Rules

Number validators support Go generics. Use Num[T]() for any numeric type.

Rule Description
Required() Field must be present
RequiredIf(fn) Required if condition is met
RequiredUnless(fn) Required unless condition is met
Min(n) Minimum value
Max(n) Maximum value
Between(min, max) Value must be between min and max
Positive() Must be greater than 0
Negative() Must be less than 0
Integer() Must be a whole number
MultipleOf(n) Must be a multiple of value
Step(n) Alias for MultipleOf
MinDigits(n) Minimum number of digits
MaxDigits(n) Maximum number of digits
LessThan(field) Must be less than another field
GreaterThan(field) Must be greater than another field
LessThanOrEqual(field) Must be ≤ another field
GreaterThanOrEqual(field) Must be ≥ another field
In(values...) Must be one of specified values
NotIn(values...) Must NOT be one of specified values
Exists(table, column) Value must exist in database
Unique(table, column, ignore) Value must be unique in database
Coerce() Coerce string to number
Custom(fn) Custom validation function
Nullable() Allow null values
Default(value) Set default value if nil
Number Examples
// Shorthand constructors
valet.Float()   // Num[float64]()
valet.Int()     // Num[int]()
valet.Int64()   // Num[int64]()

// Basic validations
valet.Float().Required().Min(0).Max(100)
valet.Int().Positive()
valet.Float().Between(0, 1)

// With custom messages
valet.Int().Min(18, "Must be at least 18 years old")

// Field comparisons
valet.Float().LessThan("max_price")
valet.Float().GreaterThan("min_price")

// Database checks
valet.Int().Exists("products", "id")
Boolean Rules
Rule Description
Required() Field must be present
RequiredIf(fn) Required if condition is met
RequiredUnless(fn) Required unless condition is met
True() Must be true
False() Must be false
Coerce() Coerce string to boolean
Custom(fn) Custom validation function
Nullable() Allow null values
Default(value) Set default value if nil
Boolean Examples
// Terms acceptance
valet.Bool().Required().True("You must accept the terms")

// Coerce from string ("true", "1", "yes", "on" -> true)
valet.Bool().Coerce().Required()

// Default value
valet.Bool().Default(false)
Array Rules
Rule Description
Required() Field must be present and non-empty
RequiredIf(fn) Required if condition is met
RequiredUnless(fn) Required unless condition is met
Min(n) Minimum array length
Max(n) Maximum array length
Length(n) Exact array length
Nonempty() Array must have at least one element
Of(validator) Validate each element with schema
Unique() All elements must be unique
Distinct() Alias for Unique
Contains(values...) Must contain specified values
DoesntContain(values...) Must NOT contain specified values
Exists(table, column) All elements must exist in database
Concurrent(workers) Enable concurrent element validation
Custom(fn) Custom validation function
Nullable() Allow null values
Array Examples
// Array of strings (emails)
valet.Array().Of(valet.String().Email())

// Array of numbers
valet.Array().Min(1).Of(valet.Float().Positive())

// Array of objects
valet.Array().Of(valet.Object().Shape(valet.Schema{
    "product_id": valet.Int().Required().Exists("products", "id"),
    "quantity":   valet.Int().Required().Positive(),
}))

// Unique values
valet.Array().Unique()

// Concurrent validation for large arrays
valet.Array().Concurrent(4).Of(valet.Object().Shape(schema))
Object Rules
Rule Description
Required() Field must be present
RequiredIf(fn) Required if condition is met
RequiredUnless(fn) Required unless condition is met
Shape(schema) Define nested validation schema
Strict() Fail on unknown keys
Passthrough() Allow unknown keys (default)
Pick(fields...) Create validator with only specified fields
Omit(fields...) Create validator excluding specified fields
Partial() Make all fields optional
Extend(schema) Extend schema with additional fields
Merge(validator) Merge two object validators
Custom(fn) Custom validation function
Nullable() Allow null values
Object Examples
// Nested object
valet.Object().Shape(valet.Schema{
    "street":  valet.String().Required(),
    "city":    valet.String().Required(),
    "zip":     valet.String().Required().Digits(5),
    "country": valet.String().Required().In("US", "CA", "UK"),
})

// Strict mode (fail on unknown fields)
valet.Object().Strict().Shape(schema)

// Pick only specific fields
userSchema.Pick("id", "name")

// Omit fields
userSchema.Omit("password")

// Make all fields optional (for PATCH requests)
userSchema.Partial()

// Extend schema
baseSchema.Extend(valet.Schema{
    "email": valet.String().Email(),
})
File Rules

For validating file uploads (*multipart.FileHeader).

Rule Description
Required() File must be present
RequiredIf(fn) Required if condition is met
RequiredUnless(fn) Required unless condition is met
Min(bytes) Minimum file size
Max(bytes) Maximum file size
Mimes(types...) Allowed MIME types
Extensions(exts...) Allowed file extensions
Image() Must be an image file
Dimensions(opts) Image dimension constraints
Custom(fn) Custom validation function
Nullable() Allow null values
File Examples
// Image upload
valet.File().Required().Image().Max(5 * 1024 * 1024)

// PDF documents
valet.File().Mimes("application/pdf").Max(10 * 1024 * 1024)

// Image with dimensions
valet.File().Image().Dimensions(&valet.ImageDimensions{
    MinWidth:  200,
    MaxWidth:  2000,
    MinHeight: 200,
    MaxHeight: 2000,
})

// Avatar with aspect ratio
valet.File().Image().Dimensions(&valet.ImageDimensions{
    MinWidth: 100,
    Ratio:    "1/1",  // Square
})
Schema Helpers
valet.String().
    Required().                              // Field must be present and non-empty
    RequiredIf(func(d DataObject) bool {     // Required if condition is met
        return d["type"] == "premium"
    }).
    RequiredUnless(func(d DataObject) bool { // Required unless condition is met
        return d["role"] == "guest"
    }).
    Min(5).                                  // Minimum length (UTF-8 aware)
    Max(100).                                // Maximum length
    Length(10).                              // Exact length
    Regex(`^[a-zA-Z]+$`).                    // Must match regex pattern
    Email().                                 // Must be valid email format
    URL().                                   // Must be valid URL
    Alpha().                                 // Must contain only letters
    AlphaNumeric().                          // Must contain only letters and numbers
    Numeric().                               // Must contain only digits
    In("a", "b", "c").                       // Must be one of these values
    NotIn("x", "y", "z").                    // Must NOT be one of these values
    StartsWith("prefix").                    // Must start with string
    EndsWith("suffix").                      // Must end with string
    Contains("substring").                   // Must contain string
    UUID().                                  // Must be valid UUID
    Trim().                                  // Trim whitespace before validation
    ToLower().                               // Convert to lowercase before validation
    ToUpper().                               // Convert to uppercase before validation
    Exists("table", "column").               // DB existence check
    Exists("table", "column", where...).     // DB existence check with conditions
    Unique("table", "column", nil).          // DB uniqueness check
    Unique("table", "column", ignoreValue).  // DB uniqueness check (ignore value for updates)
    Custom(func(v string, lookup Lookup) error { // Custom validation
        if v == "forbidden" {
            return errors.New("this value is not allowed")
        }
        return nil
    }).
    Message("required", "Name is mandatory").  // Custom error message
    Nullable()                                 // Allow null values
Number Validators
// Float64 (default for JSON numbers)
valet.Float().
    Required().
    Min(0).                    // Minimum value
    Max(1000).                 // Maximum value
    Positive().                // Must be > 0
    Negative().                // Must be < 0
    NonNegative().             // Must be >= 0
    NonPositive().             // Must be <= 0
    MultipleOf(5).             // Must be multiple of value
    Int().                     // Must be integer (no decimals)
    In(1.0, 2.0, 3.0).         // Must be one of these values
    NotIn(0, -1).              // Must NOT be one of these values
    Exists("products", "id").  // DB existence check
    Unique("orders", "num", nil). // DB uniqueness check
    Custom(func(v float64, lookup Lookup) error {
        maxPrice := lookup("max_price").Float()
        if v > maxPrice {
            return errors.New("price exceeds maximum")
        }
        return nil
    }).
    Coerce().                  // Coerce string to number
    Nullable()

// Integer types
valet.Int().Required().Min(0).Max(100)
valet.Int64().Required().Min(0)
Boolean Validator
valet.Bool().
    Required().
    RequiredIf(func(d DataObject) bool {
        return d["show_terms"] == true
    }).
    True().                    // Must be true
    False().                   // Must be false
    Custom(func(v bool, lookup Lookup) error {
        termsShown := lookup("terms_shown").Bool()
        if termsShown && !v {
            return errors.New("you must agree to the terms")
        }
        return nil
    }).
    Coerce().                  // Coerce string "true"/"false" to bool
    Nullable()
Object Validator
valet.Object().
    Required().
    Shape(valet.Schema{      // Define nested schema
        "street":  valet.String().Required(),
        "city":    valet.String().Required(),
        "zip":     valet.String().Required().Length(5),
        "country": valet.String().Required(),
    }).
    Strict().                  // Fail on unknown keys
    Passthrough().             // Allow unknown keys (default)
    Custom(func(v DataObject, lookup Lookup) error {
        // Custom object validation
        return nil
    }).
    Nullable()
Array Validator
valet.Array().
    Required().
    Min(1).                    // Minimum length
    Max(10).                   // Maximum length
    Length(5).                 // Exact length
    Unique().                  // All elements must be unique
    Of(valet.String().Email()). // Validate each element
    Of(valet.Object().Shape(valet.Schema{  // Array of objects
        "product_id": valet.Float().Required().Exists("products", "id"),
        "quantity":   valet.Float().Required().Positive(),
    })).
    Exists("tags", "id").      // Check all elements exist in DB
    Custom(func(v []any, lookup Lookup) error {
        // Custom array validation
        return nil
    }).
    Nullable()
Schema Helpers

Helper functions for common validation patterns.

Helper Description
Enum(values...) Value must be one of predefined options
EnumInt(values...) Integer must be one of predefined options
Literal(value) Value must be exactly the specified value
Union(validators...) Value must match one of multiple validators
Optional(validator) Make any validator optional
Schema Helper Examples
// String enum
valet.Enum("draft", "published", "archived")

// Integer enum
valet.EnumInt(1, 2, 3, 4, 5)

// Literal value
valet.Literal("active")

// Union (accept multiple types)
valet.Union(
    valet.String().Email(),
    valet.String().Regex(`^\+\d{10,15}$`),  // Phone
)

// Optional
valet.Optional(valet.String().Email())

Custom Error Messages

Valet supports flexible custom error messages with two approaches:

Inline String Messages

Pass a string message directly to any validation method:

valet.String().
    Required("Name is required").
    Min(3, "Name must be at least 3 characters").
    Max(50, "Name cannot exceed 50 characters")
Dynamic Message Functions

Use MessageFunc for dynamic messages with access to validation context:

valet.String().Required(func(ctx valet.MessageContext) string {
    return fmt.Sprintf("The %s field is required", ctx.Field)
})
MessageContext Fields

The MessageContext provides rich context for dynamic messages:

type MessageContext struct {
    Field string       // Field name (e.g., "email")
    Path  string       // Full path (e.g., "users.0.email")
    Index int          // Array index if inside array (-1 otherwise)
    Value any          // The actual value being validated
    Rule  string       // The validation rule that failed
    Param any          // Rule parameter (e.g., 3 for Min(3))
    Data  DataAccessor // Root data with Get() method
}
Using Data.Get()

Access other fields from the root data in your message:

valet.String().Required(func(ctx valet.MessageContext) string {
    userName := ctx.Data.Get("user.name").String()
    return fmt.Sprintf("Email is required for user %s", userName)
})

// Access array items
valet.Float().Positive(func(ctx valet.MessageContext) string {
    itemName := ctx.Data.Get(fmt.Sprintf("items.%d.name", ctx.Index)).String()
    return fmt.Sprintf("Price for '%s' must be positive", itemName)
})
Using Message() Method

Set messages for specific rules using the Message() method:

valet.String().
    Required().
    Email().
    Message("required", "Please enter your email").
    Message("email", "Please enter a valid email address")

Database Validation

Setting Up a DB Checker
Using FuncAdapter (Simple)
checker := valet.FuncAdapter(func(ctx context.Context, table, column string, values []any, wheres []valet.WhereClause) (map[any]bool, error) {
    result := make(map[any]bool)
    // Query your database and populate result
    // result[value] = true if value exists
    return result, nil
})

err := valet.ValidateWithDB(ctx, data, schema, checker)
Using SQL Adapter
import "database/sql"

db, _ := sql.Open("mysql", "user:password@/dbname")
checker := valet.NewSQLAdapter(db)
Using GORM Adapter
type GormDB struct {
    db *gorm.DB
}

func (g *GormDB) Raw(ctx context.Context, sql string, values ...interface{}) valet.GormResult {
    return g.db.WithContext(ctx).Raw(sql, values...)
}

checker := valet.NewGormAdapter(&GormDB{db: gormDB})
Using SQLX Adapter
db, _ := sqlx.Connect("postgres", dsn)
checker := valet.NewSQLXAdapter(db)
Using Bun Adapter
sqldb, _ := sql.Open("postgres", dsn)
db := bun.NewDB(sqldb, pgdialect.New())
checker := valet.NewBunAdapter(db)
Where Clauses

Add conditions to database checks:

// Equal
valet.WhereEq("status", "active")

// Not equal
valet.WhereNot("deleted", true)

// Custom operator
valet.Where("stock", ">", 0)

// Example usage
valet.Int().Exists("products", "id",
    valet.WhereEq("status", "active"),
    valet.Where("stock", ">", 0),
)
Batched Queries

Valet automatically batches database queries to prevent N+1 problems:

// This validates 100 items but only makes 1 query per table
schema := valet.Schema{
    "items": valet.Array().Of(valet.Object().Shape(valet.Schema{
        "product_id": valet.Int().Exists("products", "id"),
        "category":   valet.String().Exists("categories", "slug"),
    })),
}

Lookup Function

Access other fields during validation:

valet.Float().Custom(func(v float64, lookup valet.Lookup) error {
    // Get another field's value
    maxPrice := lookup("settings.max_price").Float()
    minQty := lookup("min_quantity").Int()
    isActive := lookup("is_active").Bool()
    name := lookup("name").String()
    
    // Check if field exists
    if lookup("optional_field").Exists() {
        // ...
    }
    
    // Get nested value
    city := lookup("address").Get("city").String()
    
    return nil
})

Performance

Benchmark Results

Tested on Intel Core i7-1355U, Go 1.21+

Validator ns/op allocs/op
String_Required ~130 3
String_Email ~535 3
String_UUID ~783 7
Number_Required ~262 5
Boolean_Required ~85 2
Object_Shape ~1,498 14
Array_Of_Object ~3,425 46
Performance Optimizations
  • sync.Pool for object reuse
  • Regex caching with thread-safe cache
  • Parallel DB queries for multi-table checks
  • Batched queries to prevent N+1 problem
  • Pre-compiled regex for common patterns (UUID, MAC, ULID, etc.)
  • Zero-allocation validators for simple checks

Examples

API Request Validation
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {
    var data map[string]any
    json.NewDecoder(r.Body).Decode(&data)
    
    schema := valet.Schema{
        "username": valet.String().Required().Min(3).Max(50).
            AlphaNumeric().
            Unique("users", "username", nil),
        "email": valet.String().Required().Email().
            Unique("users", "email", nil),
        "password": valet.String().Required().Min(8).
            Regex(`[A-Z]`, "Must contain uppercase").
            Regex(`[0-9]`, "Must contain number"),
        "age": valet.Float().Required().Min(18).Max(120),
    }
    
    if err := valet.ValidateWithDB(r.Context(), data, schema, dbChecker); err != nil {
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(err.Errors)
        return
    }
    
    // Create user...
}
E-commerce Order
schema := valet.Schema{
    "customer_id": valet.Int().Required().Exists("users", "id"),
    "shipping_address": valet.Object().Required().Shape(valet.Schema{
        "street":  valet.String().Required().Min(5),
        "city":    valet.String().Required(),
        "zip":     valet.String().Required().Digits(5),
        "country": valet.String().Required().In("US", "CA", "UK"),
    }),
    "items": valet.Array().Required().Min(1).Of(valet.Object().Shape(valet.Schema{
        "product_id": valet.Int().Required().Exists("products", "id",
            valet.WhereEq("status", "active"),
        ),
        "quantity": valet.Int().Required().Positive().Max(100),
    })),
    "coupon_code": valet.String().Nullable().Exists("coupons", "code",
        valet.WhereEq("active", true),
    ),
}
Password Confirmation
schema := valet.Schema{
    "password": valet.String().Required().Min(8),
    "password_confirmation": valet.String().Required().SameAs("password"),
}
Conditional Validation
schema := valet.Schema{
    "payment_type": valet.String().Required().In("card", "bank", "crypto"),
    
    "card_number": valet.String().
        RequiredIf(func(d valet.DataObject) bool {
            return d["payment_type"] == "card"
        }).
        Digits(16),
    
    "bank_account": valet.String().
        RequiredIf(func(d valet.DataObject) bool {
            return d["payment_type"] == "bank"
        }),
    
    "wallet_address": valet.String().
        RequiredIf(func(d valet.DataObject) bool {
            return d["payment_type"] == "crypto"
        }),
}
Dynamic Error Messages with Array Context
schema := valet.Schema{
    "items": valet.Array().Of(valet.Object().Shape(valet.Schema{
        "name": valet.String().Required(),
        "price": valet.Float().Required().Positive(func(ctx valet.MessageContext) string {
            name := ctx.Data.Get(fmt.Sprintf("items.%d.name", ctx.Index)).String()
            return fmt.Sprintf("Price for '%s' must be positive", name)
        }),
    })),
}

Notes

JSON Number Types

When parsing JSON in Go, all numbers become float64. Use valet.Float() for JSON number validation:

// JSON: {"age": 25}
// In Go: data["age"] is float64(25)
schema := valet.Schema{
    "age": valet.Float().Required().Integer(), // Validates as integer
}
Thread Safety

All validators are thread-safe and can be reused across goroutines. The internal caches (regex, sync.Pool) are protected by mutexes.


License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Documentation

Overview

Package valet provides a high-performance, Zod-inspired validation library for Go with a fluent builder API.

It works with dynamic map data (map[string]any) - perfect for validating JSON payloads, API requests, and configuration data without requiring pre-defined structs.

Features

  • Fluent Builder API - Zod-like chainable validation rules
  • Zero Dependencies - Only uses Go standard library
  • High Performance - Optimized with sync.Pool, regex caching, and parallel execution
  • Type-Safe Generics - Numeric validators use Go generics
  • Nested Validation - Validate deeply nested objects and arrays
  • Conditional Validation - RequiredIf and RequiredUnless rules
  • Custom Validators - Add your own validation logic
  • Custom Error Messages - Inline messages with dynamic template functions
  • Integrated DB Validation - Exists/Unique checks with batched queries
  • Multiple DB Adapters - Support for database/sql, GORM, sqlx, Bun

Quick Start

package main

import (
    "encoding/json"
    "fmt"
    "github.com/ezartsh/valet"
)

func main() {
    jsonData := []byte(`{"name": "John", "email": "john@example.com", "age": 25}`)

    var data map[string]any
    json.Unmarshal(jsonData, &data)

    schema := valet.Schema{
        "name":  valet.String().Required().Min(2).Max(100),
        "email": valet.String().Required().Email(),
        "age":   valet.Float().Required().Min(18).Max(120),
    }

    if err := valet.Validate(data, schema); err != nil {
        fmt.Println("Validation failed:", err.Errors)
    }
}

String Validator

The String validator provides comprehensive string validation:

valet.String().
    Required().              // Field must be present and non-empty
    Min(3).                  // Minimum length
    Max(100).                // Maximum length
    Length(10).              // Exact length
    Email().                 // Valid email format
    URL().                   // Valid URL format
    UUID().                  // Valid UUID format
    ULID().                  // Valid ULID format
    IP().                    // Valid IP address (IPv4 or IPv6)
    IPv4().                  // Valid IPv4 address
    IPv6().                  // Valid IPv6 address
    MAC().                   // Valid MAC address
    JSON().                  // Valid JSON string
    Base64().                // Valid Base64 encoding
    HexColor().              // Valid hex color code
    Alpha().                 // Letters only
    AlphaNumeric().          // Letters and numbers only
    AlphaDash().             // Letters, numbers, dashes, underscores
    ASCII().                 // ASCII characters only
    Digits(4).               // Digits only with exact length
    Regex(`^[A-Z]+$`).       // Must match pattern
    NotRegex(`<script>`).    // Must NOT match pattern
    In("a", "b", "c").       // Must be one of values
    NotIn("x", "y").         // Must NOT be one of values
    StartsWith("pre_").      // Must start with prefix
    EndsWith(".pdf").        // Must end with suffix
    DoesntStartWith("_").    // Must NOT start with prefixes
    DoesntEndWith(".tmp").   // Must NOT end with suffixes
    Contains("@").           // Must contain substring
    Includes("http", "://"). // Must contain all substrings
    SameAs("password").      // Must equal another field
    DifferentFrom("old").    // Must differ from another field
    Trim().                  // Trim whitespace before validation
    Lowercase().             // Convert to lowercase
    Uppercase().             // Convert to uppercase
    Transform(fn).           // Custom transformation
    Exists("table", "col").  // Must exist in database
    Unique("table", "col", nil). // Must be unique in database
    Custom(fn).              // Custom validation function
    Nullable()               // Allow null values

Number Validators

Number validators support Go generics for type-safe validation:

// Shorthand constructors
valet.Float()   // Num[float64]() - for JSON numbers
valet.Int()     // Num[int]()
valet.Int64()   // Num[int64]()

valet.Float().
    Required().              // Field must be present
    Min(0).                  // Minimum value
    Max(100).                // Maximum value
    Between(0, 100).         // Value must be between min and max
    Positive().              // Must be > 0
    Negative().              // Must be < 0
    Integer().               // Must be whole number
    MultipleOf(5).           // Must be multiple of value
    Step(0.5).               // Alias for MultipleOf
    MinDigits(4).            // Minimum number of digits
    MaxDigits(6).            // Maximum number of digits
    LessThan("max").         // Must be < another field
    GreaterThan("min").      // Must be > another field
    LessThanOrEqual("max").  // Must be <= another field
    GreaterThanOrEqual("min"). // Must be >= another field
    In(1, 2, 3).             // Must be one of values
    NotIn(0, -1).            // Must NOT be one of values
    Exists("table", "id").   // Must exist in database
    Unique("table", "num", nil). // Must be unique
    Coerce().                // Coerce string to number
    Custom(fn).              // Custom validation
    Nullable()               // Allow null values

Boolean Validator

The Bool validator handles boolean values:

valet.Bool().
    Required().              // Field must be present
    True().                  // Must be true
    False().                 // Must be false
    Coerce().                // Coerce "true"/"false" strings
    Default(false).          // Default value if nil
    Nullable()               // Allow null values

Array Validator

The Array validator handles slices with element validation:

valet.Array().
    Required().              // Field must be present
    Min(1).                  // Minimum length
    Max(10).                 // Maximum length
    Length(5).               // Exact length
    Nonempty().              // Must have at least one element
    Of(valet.String()).      // Validate each element
    Unique().                // All elements must be unique
    Distinct().              // Alias for Unique
    Contains("a", "b").      // Must contain values
    DoesntContain("x").      // Must NOT contain values
    Exists("table", "id").   // All elements must exist in DB
    Concurrent(4).           // Concurrent element validation
    Custom(fn).              // Custom validation
    Nullable()               // Allow null values

Object Validator

The Object validator handles nested structures:

valet.Object().
    Required().              // Field must be present
    Shape(valet.Schema{      // Define nested schema
        "name": valet.String().Required(),
        "age":  valet.Float().Required(),
    }).
    Strict().                // Fail on unknown keys
    Passthrough().           // Allow unknown keys (default)
    Pick("name", "age").     // Only validate these fields
    Omit("password").        // Exclude these fields
    Partial().               // Make all fields optional
    Extend(schema).          // Add more fields
    Merge(other).            // Merge two validators
    Custom(fn).              // Custom validation
    Nullable()               // Allow null values

File Validator

The File validator handles multipart file uploads:

valet.File().
    Required().              // File must be present
    Min(1024).               // Minimum size in bytes
    Max(5 * 1024 * 1024).    // Maximum size (5MB)
    Mimes("image/jpeg", "image/png"). // Allowed MIME types
    Extensions(".jpg", ".png"). // Allowed extensions
    Image().                 // Must be image file
    Dimensions(&valet.ImageDimensions{
        MinWidth: 100,
        MaxWidth: 2000,
        Ratio:    "16/9",
    }).
    Custom(fn).              // Custom validation
    Nullable()               // Allow null values

Schema Helpers

Helper functions for common patterns:

valet.Enum("draft", "published")  // String enum
valet.EnumInt(1, 2, 3, 4, 5)      // Integer enum
valet.Literal("active")           // Exact value match
valet.Union(validator1, validator2) // Match any validator
valet.Optional(validator)         // Make validator optional

Custom Error Messages

Valet supports inline custom error messages:

// String messages
valet.String().Required("Name is required")
valet.String().Min(3, "Name must be at least 3 characters")

// Dynamic messages with MessageFunc
valet.String().Required(func(ctx valet.MessageContext) string {
    return fmt.Sprintf("The %s field is required", ctx.Field)
})

The MessageContext provides rich context for dynamic messages:

type MessageContext struct {
    Field string       // Field name (e.g., "email")
    Path  string       // Full path (e.g., "users.0.email")
    Index int          // Array index if inside array (-1 otherwise)
    Value any          // The actual value being validated
    Rule  string       // The validation rule that failed
    Param any          // Rule parameter (e.g., 3 for Min(3))
    Data  DataAccessor // Root data with Get() method
}

Access other fields using Data.Get():

valet.Float().Positive(func(ctx valet.MessageContext) string {
    name := ctx.Data.Get(fmt.Sprintf("items.%d.name", ctx.Index)).String()
    return fmt.Sprintf("Price for '%s' must be positive", name)
})

Conditional Validation

Validate fields based on conditions:

valet.String().RequiredIf(func(data valet.DataObject) bool {
    return data["type"] == "premium"
})

valet.String().RequiredUnless(func(data valet.DataObject) bool {
    return data["is_guest"] == true
})

Lookup Function

Access other fields in custom validators:

valet.Float().Custom(func(v float64, lookup valet.Lookup) error {
    maxPrice := lookup("settings.max_price").Float()
    if v > maxPrice {
        return errors.New("price exceeds maximum")
    }
    return nil
})

Database Validation

The package supports database validation with multiple adapters:

// SQL Adapter
checker := valet.NewSQLAdapter(db)

// GORM Adapter
checker := valet.NewGormAdapter(gormDB)

// SQLX Adapter
checker := valet.NewSQLXAdapter(sqlxDB)

// Bun Adapter
checker := valet.NewBunAdapter(bunDB)

// Function Adapter
checker := valet.FuncAdapter(func(ctx context.Context, table, column string,
    values []any, wheres []valet.WhereClause) (map[any]bool, error) {
    // Custom implementation
})

Use with schema:

schema := valet.Schema{
    "email": valet.String().Required().Unique("users", "email", nil),
    "category_id": valet.Float().Required().Exists("categories", "id",
        valet.WhereEq("status", "active"),
    ),
}

err := valet.ValidateWithDB(ctx, data, schema, checker)

Where Clauses

Add conditions to database checks:

valet.WhereEq("status", "active")    // status = 'active'
valet.WhereNot("deleted", true)      // deleted != true
valet.Where("stock", ">", 0)         // stock > 0

Performance

The package is optimized for high performance:

  • sync.Pool for object reuse
  • Regex caching with thread-safe cache
  • Pre-compiled regex for UUID, MAC, ULID, etc.
  • Parallel DB queries for multi-table checks
  • Batched queries to prevent N+1 problem
  • Zero-allocation validators for simple checks
  • Context cancellation support
Example (ArrayValidation)
package main

import (
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	data := map[string]any{
		"tags": []any{"go", "validation", "api"},
		"items": []any{
			map[string]any{"name": "Item 1", "price": float64(10.99)},
			map[string]any{"name": "Item 2", "price": float64(20.50)},
		},
	}

	schema := valet.Schema{
		"tags": valet.Array().Required().Min(1).Of(valet.String().Min(2)),
		"items": valet.Array().Required().Min(1).Of(valet.Object().Shape(valet.Schema{
			"name":  valet.String().Required(),
			"price": valet.Float().Required().Positive(),
		})),
	}

	if err := valet.Validate(data, schema); err != nil {
		fmt.Println("Validation failed:", err.Errors)
	} else {
		fmt.Println("Validation passed!")
	}
}
Output:

Validation passed!
Example (BasicValidation)
package main

import (
	"encoding/json"
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	// Parse JSON into map
	jsonData := []byte(`{
		"name": "John Doe",
		"email": "john@example.com",
		"age": 25
	}`)

	var data map[string]any
	json.Unmarshal(jsonData, &data)

	// Define validation schema
	schema := valet.Schema{
		"name":  valet.String().Required().Min(2).Max(100),
		"email": valet.String().Required().Email(),
		"age":   valet.Float().Required().Min(18).Max(120),
	}

	// Validate
	if err := valet.Validate(data, schema); err != nil {
		fmt.Println("Validation failed:", err.Errors)
	} else {
		fmt.Println("Validation passed!")
	}
}
Output:

Validation passed!
Example (ConditionalValidation)
package main

import (
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	data := map[string]any{
		"payment_type": "card",
		"card_number":  "1234567890123456",
	}

	schema := valet.Schema{
		"payment_type": valet.String().Required().In("card", "bank", "crypto"),
		"card_number": valet.String().
			RequiredIf(func(d valet.DataObject) bool {
				return d["payment_type"] == "card"
			}).
			Regex(`^\d{16}$`),
	}

	if err := valet.Validate(data, schema); err != nil {
		fmt.Println("Validation failed:", err.Errors)
	} else {
		fmt.Println("Validation passed!")
	}
}
Output:

Validation passed!
Example (CustomValidation)
package main

import (
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	data := map[string]any{
		"password":         "SecurePass123",
		"confirm_password": "SecurePass123",
	}

	schema := valet.Schema{
		"password": valet.String().Required().Min(8),
		"confirm_password": valet.String().Required().
			Custom(func(v string, lookup valet.Lookup) error {
				password := lookup("password").String()
				if v != password {
					return fmt.Errorf("passwords do not match")
				}
				return nil
			}),
	}

	if err := valet.Validate(data, schema); err != nil {
		fmt.Println("Validation failed:", err.Errors)
	} else {
		fmt.Println("Validation passed!")
	}
}
Output:

Validation passed!
Example (DatabaseValidation)
package main

import (
	"context"
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	// Create a mock database checker
	checker := valet.FuncAdapter(func(ctx context.Context, table, column string, values []any, wheres []valet.WhereClause) (map[any]bool, error) {
		// Simulate database lookup
		existing := map[any]bool{
			float64(1): true,
			float64(2): true,
			float64(3): true,
		}

		result := make(map[any]bool)
		for _, v := range values {
			result[v] = existing[v]
		}
		return result, nil
	})

	data := map[string]any{
		"user_id": float64(1),
	}

	schema := valet.Schema{
		"user_id": valet.Float().Required().Exists("users", "id"),
	}

	if err := valet.ValidateWithDB(context.Background(), data, schema, checker); err != nil {
		fmt.Println("Validation failed:", err.Errors)
	} else {
		fmt.Println("Validation passed!")
	}
}
Output:

Validation passed!
Example (ErrorHandling)
package main

import (
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	data := map[string]any{
		"name":  "J",          // Too short
		"email": "invalid",    // Invalid email
		"age":   float64(150), // Too high
	}

	schema := valet.Schema{
		"name":  valet.String().Required().Min(2).Max(100),
		"email": valet.String().Required().Email(),
		"age":   valet.Float().Required().Min(18).Max(120),
	}

	if err := valet.Validate(data, schema); err != nil {
		fmt.Println("Has errors:", err.HasErrors())
		fmt.Println("Error count:", len(err.Errors))
	}
}
Output:

Has errors: true
Error count: 3
Example (NestedObjectValidation)
package main

import (
	"fmt"

	"github.com/ezartsh/valet"
)

func main() {
	data := map[string]any{
		"user": map[string]any{
			"name":  "John",
			"email": "john@example.com",
		},
		"address": map[string]any{
			"street": "123 Main St",
			"city":   "New York",
			"zip":    "10001",
		},
	}

	schema := valet.Schema{
		"user": valet.Object().Required().Shape(valet.Schema{
			"name":  valet.String().Required().Min(2),
			"email": valet.String().Required().Email(),
		}),
		"address": valet.Object().Required().Shape(valet.Schema{
			"street": valet.String().Required().Min(5),
			"city":   valet.String().Required(),
			"zip":    valet.String().Required().Length(5),
		}),
	}

	if err := valet.Validate(data, schema); err != nil {
		fmt.Println("Validation failed:", err.Errors)
	} else {
		fmt.Println("Validation passed!")
	}
}
Output:

Validation passed!

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrValidation       = errors.New("validation failed")
	ErrRequired         = errors.New("field is required")
	ErrInvalidType      = errors.New("invalid type")
	ErrMinLength        = errors.New("value is too short")
	ErrMaxLength        = errors.New("value is too long")
	ErrMinValue         = errors.New("value is too small")
	ErrMaxValue         = errors.New("value is too large")
	ErrInvalidEmail     = errors.New("invalid email format")
	ErrInvalidURL       = errors.New("invalid URL format")
	ErrInvalidFormat    = errors.New("invalid format")
	ErrNotInAllowed     = errors.New("value not in allowed list")
	ErrInDisallowed     = errors.New("value in disallowed list")
	ErrNotExists        = errors.New("value does not exist")
	ErrAlreadyExists    = errors.New("value already exists")
	ErrInvalidFile      = errors.New("invalid file")
	ErrFileTooSmall     = errors.New("file is too small")
	ErrFileTooLarge     = errors.New("file is too large")
	ErrInvalidMimeType  = errors.New("invalid MIME type")
	ErrInvalidExtension = errors.New("invalid file extension")
	ErrNotImage         = errors.New("file is not an image")
	ErrInvalidDimension = errors.New("invalid image dimensions")
)

Validation error types

View Source
var (
	ImageMimes = []string{
		"image/jpeg",
		"image/png",
		"image/gif",
		"image/bmp",
		"image/svg+xml",
		"image/webp",
		"image/tiff",
	}
	DocumentMimes = []string{
		"application/pdf",
		"application/msword",
		"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
		"application/vnd.ms-excel",
		"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
		"text/plain",
	}
	VideoMimes = []string{
		"video/mp4",
		"video/mpeg",
		"video/quicktime",
		"video/x-msvideo",
		"video/webm",
	}
	AudioMimes = []string{
		"audio/mpeg",
		"audio/wav",
		"audio/ogg",
		"audio/mp4",
		"audio/webm",
	}
)

Common MIME type groups

View Source
var (
	ErrNilDBConnection = errors.New("database connection is nil")
)

Common errors

Functions

func BuildPath

func BuildPath(parts ...string) string

BuildPath efficiently builds a dot-notation path using pooled builder

func GetBuilder

func GetBuilder() *strings.Builder

GetBuilder retrieves a string builder from the pool

func GetErrorMap

func GetErrorMap() map[string][]string

GetErrorMap retrieves an error map from the pool

func GetRegex

func GetRegex(pattern string) *regexp.Regexp

GetRegex returns a cached regex, compiling if necessary Panics if pattern is invalid (use for known-good patterns)

func GetStringSlice

func GetStringSlice() []string

GetStringSlice retrieves a string slice from the pool

func JoinErrors

func JoinErrors(errs []string, sep string) string

JoinErrors efficiently joins error messages using pooled builder

func PutBuilder

func PutBuilder(b *strings.Builder)

PutBuilder returns a string builder to the pool

func PutErrorMap

func PutErrorMap(m map[string][]string)

PutErrorMap returns an error map to the pool after clearing it

func PutStringSlice

func PutStringSlice(s []string)

PutStringSlice returns a string slice to the pool

func SafeParse

func SafeParse(data DataObject, schema Schema, opts ...Options) (DataObject, *ValidationError)

SafeParse returns (data, error) instead of just error

Types

type AnyValidator

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

AnyValidator accepts any value (passthrough)

func Any

func Any() *AnyValidator

Any creates a new validator that accepts any value

func (*AnyValidator) Message

func (v *AnyValidator) Message(rule, message string) *AnyValidator

Message sets custom error message for a rule

func (*AnyValidator) Nullable

func (v *AnyValidator) Nullable() *AnyValidator

Nullable allows null values

func (*AnyValidator) Required

func (v *AnyValidator) Required() *AnyValidator

Required marks the field as required

func (*AnyValidator) Validate

func (v *AnyValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type ArrayValidator

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

ArrayValidator validates array/slice values with fluent API

func Array

func Array() *ArrayValidator

Array creates a new array validator

func (*ArrayValidator) Concurrent

func (v *ArrayValidator) Concurrent(n int) *ArrayValidator

Concurrent enables parallel validation of array elements n specifies the maximum number of goroutines to use (0 = sequential)

func (*ArrayValidator) Contains

func (v *ArrayValidator) Contains(values ...any) *ArrayValidator

Contains requires array to contain the specified values

func (*ArrayValidator) ContainsWithMessage

func (v *ArrayValidator) ContainsWithMessage(message MessageArg, values ...any) *ArrayValidator

ContainsWithMessage requires array to contain specified values with custom message

func (*ArrayValidator) Custom

func (v *ArrayValidator) Custom(fn func(value []any, lookup Lookup) error) *ArrayValidator

Custom adds custom validation function

func (*ArrayValidator) Distinct

func (v *ArrayValidator) Distinct(message ...MessageArg) *ArrayValidator

Distinct is an alias for Unique (Laravel naming)

func (*ArrayValidator) DoesntContain

func (v *ArrayValidator) DoesntContain(values ...any) *ArrayValidator

DoesntContain requires array to NOT contain the specified values

func (*ArrayValidator) DoesntContainWithMessage

func (v *ArrayValidator) DoesntContainWithMessage(message MessageArg, values ...any) *ArrayValidator

DoesntContainWithMessage requires array to NOT contain specified values with custom message

func (*ArrayValidator) Exists

func (v *ArrayValidator) Exists(table, column string, wheres ...WhereClause) *ArrayValidator

Exists adds database existence check for each element

func (*ArrayValidator) GetDBChecks

func (v *ArrayValidator) GetDBChecks(fieldPath string, value any) []DBCheck

GetDBChecks returns database checks for array elements

func (*ArrayValidator) Length

func (v *ArrayValidator) Length(n int, message ...MessageArg) *ArrayValidator

Length sets exact number of elements

func (*ArrayValidator) Max

func (v *ArrayValidator) Max(n int, message ...MessageArg) *ArrayValidator

Max sets maximum number of elements

func (*ArrayValidator) Message

func (v *ArrayValidator) Message(rule string, message MessageArg) *ArrayValidator

Message sets custom error message for a rule

func (*ArrayValidator) Min

func (v *ArrayValidator) Min(n int, message ...MessageArg) *ArrayValidator

Min sets minimum number of elements

func (*ArrayValidator) Nonempty

func (v *ArrayValidator) Nonempty() *ArrayValidator

Nonempty is shorthand for Min(1)

func (*ArrayValidator) Nullable

func (v *ArrayValidator) Nullable() *ArrayValidator

Nullable allows null values

func (*ArrayValidator) Of

func (v *ArrayValidator) Of(validator Validator) *ArrayValidator

Of sets the validator for each element

func (*ArrayValidator) Required

func (v *ArrayValidator) Required(message ...MessageArg) *ArrayValidator

Required marks the field as required

func (*ArrayValidator) RequiredIf

func (v *ArrayValidator) RequiredIf(fn func(data DataObject) bool, message ...MessageArg) *ArrayValidator

RequiredIf makes field required based on condition

func (*ArrayValidator) RequiredUnless

func (v *ArrayValidator) RequiredUnless(fn func(data DataObject) bool, message ...MessageArg) *ArrayValidator

RequiredUnless makes field required unless condition is met

func (*ArrayValidator) Unique

func (v *ArrayValidator) Unique(message ...MessageArg) *ArrayValidator

Unique requires all elements to be unique

func (*ArrayValidator) Validate

func (v *ArrayValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type BoolValidator

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

BoolValidator validates boolean values with fluent API

func Bool

func Bool() *BoolValidator

Bool creates a new boolean validator

func (*BoolValidator) Coerce

func (v *BoolValidator) Coerce() *BoolValidator

Coerce attempts to convert string/number to boolean

func (*BoolValidator) Custom

func (v *BoolValidator) Custom(fn func(value bool, lookup Lookup) error) *BoolValidator

Custom adds custom validation function

func (*BoolValidator) Default

func (v *BoolValidator) Default(value bool) *BoolValidator

Default sets default value if field is empty/missing

func (*BoolValidator) False

func (v *BoolValidator) False(message ...MessageArg) *BoolValidator

False requires value to be false

func (*BoolValidator) Message

func (v *BoolValidator) Message(rule string, message MessageArg) *BoolValidator

Message sets custom error message for a rule

func (*BoolValidator) Nullable

func (v *BoolValidator) Nullable() *BoolValidator

Nullable allows null values

func (*BoolValidator) Required

func (v *BoolValidator) Required(message ...MessageArg) *BoolValidator

Required marks the field as required

func (*BoolValidator) RequiredIf

func (v *BoolValidator) RequiredIf(fn func(data DataObject) bool, message ...MessageArg) *BoolValidator

RequiredIf makes field required based on condition

func (*BoolValidator) RequiredUnless

func (v *BoolValidator) RequiredUnless(fn func(data DataObject) bool, message ...MessageArg) *BoolValidator

RequiredUnless makes field required unless condition is met

func (*BoolValidator) True

func (v *BoolValidator) True(message ...MessageArg) *BoolValidator

True requires value to be true

func (*BoolValidator) Validate

func (v *BoolValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type BunAdapter

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

BunAdapter implements DBChecker for uptrace/bun

Example

ExampleBunAdapter demonstrates using BunAdapter with Bun ORM

// In real usage with Bun:
// sqldb, _ := sql.Open("postgres", dsn)
// db := bun.NewDB(sqldb, pgdialect.New())
// checker := NewBunAdapter(db)

// Example schema with DB validation
/*
	data := DataObject{
		"author_id": float64(1),
		"status":    "published",
	}

	schema := Schema{
		"author_id": Float().Required().Exists("authors", "id"),
		"status":    String().Required().In("draft", "published", "archived"),
	}

	err := ValidateWithDB(context.Background(), data, schema, checker)
	if err != nil {
		// Handle validation errors
	}
*/

func NewBunAdapter

func NewBunAdapter(db BunQuerier) *BunAdapter

NewBunAdapter creates a checker for bun ORM

func (*BunAdapter) CheckExists

func (b *BunAdapter) CheckExists(ctx context.Context, table string, column string, values []any, wheres []WhereClause) (map[any]bool, error)

type BunQuerier

type BunQuerier interface {
	NewRaw(query string, args ...interface{}) BunRawQuery
}

BunQuerier interface for uptrace/bun compatibility

type BunRawQuery

type BunRawQuery interface {
	Scan(ctx context.Context, dest ...interface{}) error
}

type DBCheck

type DBCheck struct {
	Field    string
	Value    any
	Rule     ExistsRule
	IsUnique bool
	Ignore   any
	Message  MessageArg
}

DBCheck represents a pending database check

type DBCheckCollector

type DBCheckCollector interface {
	GetDBChecks(fieldPath string, value any) []DBCheck
}

DBCheckCollector interface for validators that can collect DB checks

type DBChecker

type DBChecker interface {
	CheckExists(ctx context.Context, table, column string, values []any, wheres []WhereClause) (map[any]bool, error)
}

DBChecker interface for database validation

type DBQuerier

type DBQuerier interface {
	QueryContext(ctx context.Context, query string, args ...any) (*sql.Rows, error)
}

DBQuerier is a minimal interface that both *sql.DB and *sql.Tx satisfy

type DataAccessor

type DataAccessor map[string]any

DataAccessor wraps DataObject to provide convenient access methods

func (DataAccessor) Get

func (d DataAccessor) Get(path string) LookupResult

Get retrieves a value from the data using dot-notation path Example: data.Get("user.profile.name") or data.Get("items.0.id")

type DataObject

type DataObject = map[string]any

DataObject represents the data to validate (parsed JSON)

func ValidateWithDBContext

func ValidateWithDBContext(ctx context.Context, data DataObject, schema Schema, opts Options) (DataObject, error)

ValidateWithDBContext validates data with full options including DB checker

type EnumValidator

type EnumValidator[T comparable] struct {
	// contains filtered or unexported fields
}

EnumValidator validates value is one of a fixed set of allowed values

func Enum

func Enum[T comparable](values ...T) *EnumValidator[T]

Enum creates a new enum validator with the allowed values

func EnumInt

func EnumInt(values ...int) *EnumValidator[int]

EnumInt creates a new enum validator for integer values (convenience function)

func (*EnumValidator[T]) Default

func (v *EnumValidator[T]) Default(value T) *EnumValidator[T]

Default sets default value if field is empty/missing

func (*EnumValidator[T]) In

func (v *EnumValidator[T]) In(values ...T) *EnumValidator[T]

In sets the allowed values (can be used instead of passing to Enum())

func (*EnumValidator[T]) Message

func (v *EnumValidator[T]) Message(rule, message string) *EnumValidator[T]

Message sets custom error message for a rule

func (*EnumValidator[T]) Nullable

func (v *EnumValidator[T]) Nullable() *EnumValidator[T]

Nullable allows null values

func (*EnumValidator[T]) Required

func (v *EnumValidator[T]) Required() *EnumValidator[T]

Required marks the field as required

func (*EnumValidator[T]) Validate

func (v *EnumValidator[T]) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type ExistsRule

type ExistsRule struct {
	Table   string
	Column  string
	Where   []WhereClause
	Message string
}

ExistsRule defines a database existence check

type FileValidator

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

FileValidator validates file uploads with fluent API

func File

func File() *FileValidator

File creates a new file validator

func (*FileValidator) Custom

func (v *FileValidator) Custom(fn func(file *multipart.FileHeader, lookup Lookup) error) *FileValidator

Custom adds custom validation function

func (*FileValidator) Dimensions

func (v *FileValidator) Dimensions(d *ImageDimensions, message ...MessageArg) *FileValidator

Dimensions sets image dimension constraints

func (*FileValidator) Extensions

func (v *FileValidator) Extensions(exts ...string) *FileValidator

Extensions sets allowed file extensions

func (*FileValidator) ExtensionsWithMessage

func (v *FileValidator) ExtensionsWithMessage(exts []string, message MessageArg) *FileValidator

ExtensionsWithMessage sets allowed file extensions with a custom message

func (*FileValidator) Image

func (v *FileValidator) Image(message ...MessageArg) *FileValidator

Image requires file to be an image

func (*FileValidator) Max

func (v *FileValidator) Max(bytes int64, message ...MessageArg) *FileValidator

Max sets maximum file size in bytes

func (*FileValidator) Message

func (v *FileValidator) Message(rule string, message MessageArg) *FileValidator

Message sets custom error message for a rule

func (*FileValidator) Mimes

func (v *FileValidator) Mimes(mimes ...string) *FileValidator

Mimes sets allowed MIME types

func (*FileValidator) MimesWithMessage

func (v *FileValidator) MimesWithMessage(mimes []string, message MessageArg) *FileValidator

MimesWithMessage sets allowed MIME types with a custom message

func (*FileValidator) Min

func (v *FileValidator) Min(bytes int64, message ...MessageArg) *FileValidator

Min sets minimum file size in bytes

func (*FileValidator) Nullable

func (v *FileValidator) Nullable() *FileValidator

Nullable allows null values

func (*FileValidator) Required

func (v *FileValidator) Required(message ...MessageArg) *FileValidator

Required marks the field as required

func (*FileValidator) RequiredIf

func (v *FileValidator) RequiredIf(fn func(data DataObject) bool, message ...MessageArg) *FileValidator

RequiredIf makes field required based on condition

func (*FileValidator) RequiredUnless

func (v *FileValidator) RequiredUnless(fn func(data DataObject) bool, message ...MessageArg) *FileValidator

RequiredUnless makes field required unless condition is met

func (*FileValidator) Validate

func (v *FileValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type FuncAdapter

type FuncAdapter func(ctx context.Context, table, column string, values []any, wheres []WhereClause) (map[any]bool, error)

FuncAdapter allows using a simple function as DBChecker

Example

ExampleFuncAdapter demonstrates using FuncAdapter for simple cases

// FuncAdapter wraps a simple function as DBChecker
checker := FuncAdapter(func(ctx context.Context, table, column string, values []any, wheres []WhereClause) (map[any]bool, error) {
	// Simulate database lookup
	existing := map[any]bool{
		float64(1): true,
		float64(2): true,
		float64(3): true,
	}

	result := make(map[any]bool)
	for _, v := range values {
		result[v] = existing[v]
	}
	return result, nil
})

// Use with validation
data := DataObject{"user_id": float64(1)}
schema := Schema{
	"user_id": Float().Required().Exists("users", "id"),
}

_ = ValidateWithDB(context.Background(), data, schema, checker)
// Output validation result

func (FuncAdapter) CheckExists

func (f FuncAdapter) CheckExists(ctx context.Context, table string, column string, values []any, wheres []WhereClause) (map[any]bool, error)

type GormAdapter

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

GormAdapter implements DBChecker for GORM-like ORMs

Example

ExampleGormAdapter demonstrates using GormAdapter with GORM

// In real usage with GORM:
// db, _ := gorm.Open(mysql.Open(dsn), &gorm.Config{})
// checker := NewGormAdapter(db)

// Example schema with DB validation
/*
	data := DataObject{
		"username": "john_doe",
		"category_id": float64(5),
	}

	schema := Schema{
		"username":    String().Required().Min(3).Unique("users", "username", nil),
		"category_id": Float().Required().Exists("categories", "id"),
	}

	err := ValidateWithDB(context.Background(), data, schema, checker)
	if err != nil {
		// Handle validation errors
	}
*/

func NewGormAdapter

func NewGormAdapter(q GormQuerier) *GormAdapter

NewGormAdapter creates an adapter for GORM-like ORMs

func (*GormAdapter) CheckExists

func (g *GormAdapter) CheckExists(ctx context.Context, table string, column string, values []any, wheres []WhereClause) (map[any]bool, error)

type GormQuerier

type GormQuerier interface {
	Raw(ctx context.Context, sql string, values ...interface{}) GormResult
}

GormQuerier is a simple interface for GORM-like ORMs

type GormResult

type GormResult interface {
	Scan(dest interface{}) error
}

GormResult represents the result of a GORM query

type ImageDimensions

type ImageDimensions struct {
	MinWidth  int
	MaxWidth  int
	MinHeight int
	MaxHeight int
	Width     int    // Exact width
	Height    int    // Exact height
	Ratio     string // e.g., "16/9", "1/1", "4/3"
}

ImageDimensions defines constraints for image dimensions

type LiteralValidator

type LiteralValidator[T comparable] struct {
	// contains filtered or unexported fields
}

LiteralValidator validates value matches exactly one specific value

func Literal

func Literal[T comparable](value T) *LiteralValidator[T]

Literal creates a new literal validator for an exact value match

func (*LiteralValidator[T]) Message

func (v *LiteralValidator[T]) Message(rule, message string) *LiteralValidator[T]

Message sets custom error message for a rule

func (*LiteralValidator[T]) Nullable

func (v *LiteralValidator[T]) Nullable() *LiteralValidator[T]

Nullable allows null values

func (*LiteralValidator[T]) Required

func (v *LiteralValidator[T]) Required() *LiteralValidator[T]

Required marks the field as required

func (*LiteralValidator[T]) Validate

func (v *LiteralValidator[T]) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type Lookup

type Lookup func(path string) LookupResult

Lookup function for accessing other fields

type LookupResult

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

LookupResult wraps a value lookup

func (LookupResult) Array

func (r LookupResult) Array() []any

Array returns the value as []any

func (LookupResult) Bool

func (r LookupResult) Bool() bool

func (LookupResult) Exists

func (r LookupResult) Exists() bool

func (LookupResult) Float

func (r LookupResult) Float() float64

func (LookupResult) Get

func (r LookupResult) Get(key string) LookupResult

Get returns a nested value by key (for objects)

func (LookupResult) Int

func (r LookupResult) Int() int64

func (LookupResult) IsArray

func (r LookupResult) IsArray() bool

IsArray returns true if the value is a slice/array

func (LookupResult) IsObject

func (r LookupResult) IsObject() bool

IsObject returns true if the value is an object/map

func (LookupResult) String

func (r LookupResult) String() string

func (LookupResult) Value

func (r LookupResult) Value() any

type MessageArg

type MessageArg interface{}

MessageArg can be either a string or a MessageFunc This allows flexible error message customization

type MessageContext

type MessageContext struct {
	Field string       // Field name (e.g., "email")
	Path  string       // Full path (e.g., "users.0.email")
	Index int          // Array index if inside array (-1 otherwise)
	Value any          // The actual value being validated
	Rule  string       // The validation rule that failed (e.g., "required", "min")
	Param any          // Rule parameter if applicable (e.g., 3 for Min(3))
	Data  DataAccessor // The root data object being validated (with Get method)
}

MessageContext provides contextual information for dynamic error messages

type MessageFunc

type MessageFunc func(ctx MessageContext) string

MessageFunc is a function that generates a custom error message

type Number

type Number interface {
	~int | ~int32 | ~int64 | ~float32 | ~float64 | ~uint | ~uint32 | ~uint64
}

Number constraint for numeric types

type NumberValidator

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

NumberValidator validates numeric values with fluent API

func Float

func Float() *NumberValidator[float64]

Float creates a float64 validator (common for JSON)

func Int

func Int() *NumberValidator[int64]

Int creates an int64 validator (common for JSON)

func Num

func Num[T Number]() *NumberValidator[T]

Num creates a new number validator

func (*NumberValidator[T]) Between

func (v *NumberValidator[T]) Between(min, max T, message ...MessageArg) *NumberValidator[T]

Between sets both minimum and maximum value (inclusive)

func (*NumberValidator[T]) Coerce

func (v *NumberValidator[T]) Coerce() *NumberValidator[T]

Coerce attempts to convert string to number

func (*NumberValidator[T]) Custom

func (v *NumberValidator[T]) Custom(fn func(value T, lookup Lookup) error) *NumberValidator[T]

Custom adds custom validation function

func (*NumberValidator[T]) Default

func (v *NumberValidator[T]) Default(value T) *NumberValidator[T]

Default sets default value if field is empty/missing

func (*NumberValidator[T]) Exists

func (v *NumberValidator[T]) Exists(table, column string, wheres ...WhereClause) *NumberValidator[T]

Exists adds database existence check

func (*NumberValidator[T]) ExistsWithMessage

func (v *NumberValidator[T]) ExistsWithMessage(message MessageArg, table, column string, wheres ...WhereClause) *NumberValidator[T]

ExistsWithMessage adds database existence check with custom message

func (*NumberValidator[T]) GetDBChecks

func (v *NumberValidator[T]) GetDBChecks(fieldPath string, value any) []DBCheck

GetDBChecks returns database checks for this field

func (*NumberValidator[T]) GreaterThan

func (v *NumberValidator[T]) GreaterThan(fieldPath string, message ...MessageArg) *NumberValidator[T]

GreaterThan validates value is greater than another field's value

func (*NumberValidator[T]) GreaterThanOrEqual

func (v *NumberValidator[T]) GreaterThanOrEqual(fieldPath string, message ...MessageArg) *NumberValidator[T]

GreaterThanOrEqual validates value is greater than or equal to another field's value

func (*NumberValidator[T]) In

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

In validates value is one of allowed values

func (*NumberValidator[T]) InWithMessage

func (v *NumberValidator[T]) InWithMessage(message MessageArg, values ...T) *NumberValidator[T]

InWithMessage validates value is one of allowed values with custom message

func (*NumberValidator[T]) Integer

func (v *NumberValidator[T]) Integer(message ...MessageArg) *NumberValidator[T]

Integer requires whole number (no decimals)

func (*NumberValidator[T]) LessThan

func (v *NumberValidator[T]) LessThan(fieldPath string, message ...MessageArg) *NumberValidator[T]

LessThan validates value is less than another field's value

func (*NumberValidator[T]) LessThanOrEqual

func (v *NumberValidator[T]) LessThanOrEqual(fieldPath string, message ...MessageArg) *NumberValidator[T]

LessThanOrEqual validates value is less than or equal to another field's value

func (*NumberValidator[T]) Max

func (v *NumberValidator[T]) Max(n T, message ...MessageArg) *NumberValidator[T]

Max sets maximum value

func (*NumberValidator[T]) MaxDigits

func (v *NumberValidator[T]) MaxDigits(n int, message ...MessageArg) *NumberValidator[T]

MaxDigits sets maximum number of digits

func (*NumberValidator[T]) Message

func (v *NumberValidator[T]) Message(rule string, message MessageArg) *NumberValidator[T]

Message sets custom error message for a rule

func (*NumberValidator[T]) Min

func (v *NumberValidator[T]) Min(n T, message ...MessageArg) *NumberValidator[T]

Min sets minimum value

func (*NumberValidator[T]) MinDigits

func (v *NumberValidator[T]) MinDigits(n int, message ...MessageArg) *NumberValidator[T]

MinDigits sets minimum number of digits

func (*NumberValidator[T]) MultipleOf

func (v *NumberValidator[T]) MultipleOf(n T, message ...MessageArg) *NumberValidator[T]

MultipleOf requires value to be multiple of n

func (*NumberValidator[T]) Negative

func (v *NumberValidator[T]) Negative(message ...MessageArg) *NumberValidator[T]

Negative requires value < 0

func (*NumberValidator[T]) NotIn

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

NotIn validates value is not one of disallowed values

func (*NumberValidator[T]) NotInWithMessage

func (v *NumberValidator[T]) NotInWithMessage(message MessageArg, values ...T) *NumberValidator[T]

NotInWithMessage validates value is not one of disallowed values with custom message

func (*NumberValidator[T]) NotRegex

func (v *NumberValidator[T]) NotRegex(pattern string, message ...MessageArg) *NumberValidator[T]

NotRegex validates string representation does NOT match pattern

func (*NumberValidator[T]) Nullable

func (v *NumberValidator[T]) Nullable() *NumberValidator[T]

Nullable allows null values

func (*NumberValidator[T]) Positive

func (v *NumberValidator[T]) Positive(message ...MessageArg) *NumberValidator[T]

Positive requires value > 0

func (*NumberValidator[T]) Regex

func (v *NumberValidator[T]) Regex(pattern string, message ...MessageArg) *NumberValidator[T]

Regex validates string representation matches pattern

func (*NumberValidator[T]) Required

func (v *NumberValidator[T]) Required(message ...MessageArg) *NumberValidator[T]

Required marks the field as required

func (*NumberValidator[T]) RequiredIf

func (v *NumberValidator[T]) RequiredIf(fn func(data DataObject) bool, message ...MessageArg) *NumberValidator[T]

RequiredIf makes field required based on condition

func (*NumberValidator[T]) RequiredUnless

func (v *NumberValidator[T]) RequiredUnless(fn func(data DataObject) bool, message ...MessageArg) *NumberValidator[T]

RequiredUnless makes field required unless condition is met

func (*NumberValidator[T]) Step

func (v *NumberValidator[T]) Step(n T, message ...MessageArg) *NumberValidator[T]

Step is an alias for MultipleOf (Zod naming)

func (*NumberValidator[T]) Unique

func (v *NumberValidator[T]) Unique(table, column string, ignore any, wheres ...WhereClause) *NumberValidator[T]

Unique adds database uniqueness check

func (*NumberValidator[T]) UniqueWithMessage

func (v *NumberValidator[T]) UniqueWithMessage(message MessageArg, table, column string, ignore any, wheres ...WhereClause) *NumberValidator[T]

UniqueWithMessage adds database uniqueness check with custom message

func (*NumberValidator[T]) Validate

func (v *NumberValidator[T]) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type ObjectValidator

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

ObjectValidator validates object/map values with fluent API

func Object

func Object() *ObjectValidator

Object creates a new object validator

func (*ObjectValidator) Custom

func (v *ObjectValidator) Custom(fn func(value DataObject, lookup Lookup) error) *ObjectValidator

Custom adds custom validation function

func (*ObjectValidator) Extend

func (v *ObjectValidator) Extend(additional Schema) *ObjectValidator

Extend creates a new validator with additional schema fields

func (*ObjectValidator) GetDBChecks

func (v *ObjectValidator) GetDBChecks(fieldPath string, value any) []DBCheck

GetDBChecks returns database checks from nested schema validators

func (*ObjectValidator) Item

func (v *ObjectValidator) Item(schema Schema) *ObjectValidator

Item is an alias for Shape (compatibility with go-validet)

func (*ObjectValidator) Merge

Merge combines this validator with another ObjectValidator

func (*ObjectValidator) Message

func (v *ObjectValidator) Message(rule string, message MessageArg) *ObjectValidator

Message sets custom error message for a rule

func (*ObjectValidator) Nullable

func (v *ObjectValidator) Nullable() *ObjectValidator

Nullable allows null values

func (*ObjectValidator) Omit

func (v *ObjectValidator) Omit(fields ...string) *ObjectValidator

Omit creates a new validator excluding the specified fields

func (*ObjectValidator) Partial

func (v *ObjectValidator) Partial() *ObjectValidator

Partial creates a new validator where all fields are optional (not required) Note: This creates new validators that wrap existing ones without the Required flag

func (*ObjectValidator) Passthrough

func (v *ObjectValidator) Passthrough() *ObjectValidator

Passthrough allows unknown keys (default behavior)

func (*ObjectValidator) Pick

func (v *ObjectValidator) Pick(fields ...string) *ObjectValidator

Pick creates a new validator with only the specified fields

func (*ObjectValidator) Required

func (v *ObjectValidator) Required(message ...MessageArg) *ObjectValidator

Required marks the field as required

func (*ObjectValidator) RequiredIf

func (v *ObjectValidator) RequiredIf(fn func(data DataObject) bool, message ...MessageArg) *ObjectValidator

RequiredIf makes field required based on condition

func (*ObjectValidator) RequiredUnless

func (v *ObjectValidator) RequiredUnless(fn func(data DataObject) bool, message ...MessageArg) *ObjectValidator

RequiredUnless makes field required unless condition is met

func (*ObjectValidator) Shape

func (v *ObjectValidator) Shape(schema Schema) *ObjectValidator

Shape sets the schema for object properties

func (*ObjectValidator) Strict

func (v *ObjectValidator) Strict(message ...MessageArg) *ObjectValidator

Strict fails validation if unknown keys are present

func (*ObjectValidator) Validate

func (v *ObjectValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type OptionalValidator

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

OptionalValidator wraps another validator and makes it optional

func Optional

func Optional(validator Validator) *OptionalValidator

Optional creates an optional wrapper for any validator

func (*OptionalValidator) GetDBChecks

func (v *OptionalValidator) GetDBChecks(fieldPath string, value any) []DBCheck

GetDBChecks returns database checks from inner validator

func (*OptionalValidator) Validate

func (v *OptionalValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type Options

type Options struct {
	AbortEarly bool
	DBChecker  DBChecker
	Context    context.Context
}

Options for validation

type PathKey

type PathKey struct {
	Previous []string
	Current  string
}

PathKey represents the current path in validation

type RegexCache

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

RegexCache provides thread-safe caching for compiled regex patterns

func (*RegexCache) GetOrCompile

func (rc *RegexCache) GetOrCompile(pattern string) (*regexp.Regexp, error)

GetOrCompile returns a cached compiled regex or compiles and caches it

type RequiredIfCondition

type RequiredIfCondition struct {
	FieldPath string
	Value     any
}

RequiredIfCondition for struct-based conditional requirement

type RequiredUnlessCondition

type RequiredUnlessCondition struct {
	FieldPath string
	Value     any
}

RequiredUnlessCondition for struct-based conditional requirement

type SQLAdapter

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

SQLAdapter implements DBChecker for standard database/sql

Example

ExampleSQLAdapter demonstrates using SQLAdapter with database/sql

// In real usage:
// db, _ := sql.Open("mysql", "user:password@/dbname")
// checker := NewSQLAdapter(db)

// Example schema with DB validation
/*
	data := DataObject{
		"email":   "user@example.com",
		"role_id": float64(1),
	}

	schema := Schema{
		"email":   String().Required().Email().Unique("users", "email", nil),
		"role_id": Float().Required().Exists("roles", "id"),
	}

	err := ValidateWithDB(context.Background(), data, schema, checker)
	if err != nil {
		// Handle validation errors
	}
*/

func NewSQLAdapter

func NewSQLAdapter(db DBQuerier) *SQLAdapter

NewSQLAdapter creates a checker for database/sql compatible connections

func NewSQLChecker

func NewSQLChecker(db *sql.DB) *SQLAdapter

NewSQLChecker is an alias for NewSQLAdapter (convenience)

func (*SQLAdapter) CheckExists

func (s *SQLAdapter) CheckExists(ctx context.Context, table string, column string, values []any, wheres []WhereClause) (map[any]bool, error)

CheckExists implements DBChecker using a single IN query

type SQLXAdapter

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

SQLXAdapter implements DBChecker for jmoiron/sqlx

Example

ExampleSQLXAdapter demonstrates using SQLXAdapter with sqlx

// In real usage with sqlx:
// db, _ := sqlx.Connect("postgres", "user=foo dbname=bar sslmode=disable")
// checker := NewSQLXAdapter(db)

// Example schema with DB validation
/*
	data := DataObject{
		"product_id": float64(100),
		"tag_ids":    []any{float64(1), float64(2), float64(3)},
	}

	schema := Schema{
		"product_id": Float().Required().Exists("products", "id"),
		"tag_ids":    Array().Required().Exists("tags", "id"),
	}

	err := ValidateWithDB(context.Background(), data, schema, checker)
	if err != nil {
		// Handle validation errors
	}
*/

func NewSQLXAdapter

func NewSQLXAdapter(db SQLXQuerier) *SQLXAdapter

NewSQLXAdapter creates a checker for sqlx

func (*SQLXAdapter) CheckExists

func (s *SQLXAdapter) CheckExists(ctx context.Context, table string, column string, values []any, wheres []WhereClause) (map[any]bool, error)

type SQLXQuerier

type SQLXQuerier interface {
	SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
}

SQLXQuerier interface for sqlx compatibility

type Schema

type Schema map[string]Validator

Schema is a map of field names to validators

type SchemaObject

type SchemaObject = map[string]any

SchemaObject is an alias for nested schema definitions

type SchemaSliceObject

type SchemaSliceObject = []SchemaObject

SchemaSliceObject is an alias for array of schema objects

type StringTransformFunc

type StringTransformFunc func(string) string

StringTransformFunc is a function that transforms a string

type StringValidator

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

StringValidator validates string values with fluent API

func String

func String() *StringValidator

String creates a new string validator

func (*StringValidator) ASCII

func (v *StringValidator) ASCII(message ...MessageArg) *StringValidator

ASCII validates string contains only ASCII characters Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Alpha

func (v *StringValidator) Alpha(message ...MessageArg) *StringValidator

Alpha validates only letters Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) AlphaDash

func (v *StringValidator) AlphaDash(message ...MessageArg) *StringValidator

AlphaDash validates string contains only alphanumeric, dash, and underscore Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) AlphaNumeric

func (v *StringValidator) AlphaNumeric(message ...MessageArg) *StringValidator

AlphaNumeric validates only letters and numbers Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Base64

func (v *StringValidator) Base64(message ...MessageArg) *StringValidator

Base64 validates string is valid base64 encoded Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Catch

func (v *StringValidator) Catch(value string) *StringValidator

Catch sets a fallback value to use if validation fails

func (*StringValidator) Contains

func (v *StringValidator) Contains(substr string, message ...MessageArg) *StringValidator

Contains validates string contains substring Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Custom

func (v *StringValidator) Custom(fn func(value string, lookup Lookup) error) *StringValidator

Custom adds custom validation function

func (*StringValidator) Default

func (v *StringValidator) Default(value string) *StringValidator

Default sets default value if field is empty/missing

func (*StringValidator) DifferentFrom

func (v *StringValidator) DifferentFrom(fieldPath string) *StringValidator

DifferentFrom validates that this field differs from another field's value

func (*StringValidator) Digits

func (v *StringValidator) Digits(length int, message ...MessageArg) *StringValidator

Digits validates string is numeric with exact length Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) DoesntEndWith

func (v *StringValidator) DoesntEndWith(suffixes ...string) *StringValidator

DoesntEndWith validates string does NOT end with any of the suffixes

func (*StringValidator) DoesntStartWith

func (v *StringValidator) DoesntStartWith(prefixes ...string) *StringValidator

DoesntStartWith validates string does NOT start with any of the prefixes

func (*StringValidator) Email

func (v *StringValidator) Email(message ...MessageArg) *StringValidator

Email validates email format Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) EndsWith

func (v *StringValidator) EndsWith(suffix string, message ...MessageArg) *StringValidator

EndsWith validates string ends with suffix Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Exists

func (v *StringValidator) Exists(table, column string, wheres ...WhereClause) *StringValidator

Exists adds database existence check

func (*StringValidator) GetDBChecks

func (v *StringValidator) GetDBChecks(fieldPath string, value any) []DBCheck

GetDBChecks returns database checks for this field

func (*StringValidator) HexColor

func (v *StringValidator) HexColor(message ...MessageArg) *StringValidator

HexColor validates hex color format (#RGB or #RRGGBB) Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) IP

func (v *StringValidator) IP(message ...MessageArg) *StringValidator

IP validates IPv4 or IPv6 address Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) IPv4

func (v *StringValidator) IPv4(message ...MessageArg) *StringValidator

IPv4 validates IPv4 address only Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) IPv6

func (v *StringValidator) IPv6(message ...MessageArg) *StringValidator

IPv6 validates IPv6 address only Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) In

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

In validates value is one of allowed values Note: To add a custom message, use .Message("in", "your message") after this call

func (*StringValidator) InWithMessage

func (v *StringValidator) InWithMessage(message MessageArg, values ...string) *StringValidator

InWithMessage validates value is one of allowed values with a custom message

func (*StringValidator) Includes

func (v *StringValidator) Includes(substrs ...string) *StringValidator

Includes validates string contains all of the specified substrings

func (*StringValidator) JSON

func (v *StringValidator) JSON(message ...MessageArg) *StringValidator

JSON validates string is valid JSON Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Length

func (v *StringValidator) Length(n int, message ...MessageArg) *StringValidator

Length sets exact length (min = max = n) Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Lowercase

func (v *StringValidator) Lowercase() *StringValidator

Lowercase converts to lowercase before validation

func (*StringValidator) MAC

func (v *StringValidator) MAC(message ...MessageArg) *StringValidator

MAC validates MAC address format Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Max

func (v *StringValidator) Max(n int, message ...MessageArg) *StringValidator

Max sets maximum length Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Message

func (v *StringValidator) Message(rule string, message MessageArg) *StringValidator

Message sets custom error message for a rule message can be a string or MessageFunc for dynamic messages

func (*StringValidator) Min

func (v *StringValidator) Min(n int, message ...MessageArg) *StringValidator

Min sets minimum length Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) NotIn

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

NotIn validates value is not one of disallowed values Note: To add a custom message, use .Message("notIn", "your message") after this call

func (*StringValidator) NotInWithMessage

func (v *StringValidator) NotInWithMessage(message MessageArg, values ...string) *StringValidator

NotInWithMessage validates value is not one of disallowed values with a custom message

func (*StringValidator) NotRegex

func (v *StringValidator) NotRegex(pattern string, message ...MessageArg) *StringValidator

NotRegex validates value does NOT match pattern Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Nullable

func (v *StringValidator) Nullable() *StringValidator

Nullable allows null values

func (*StringValidator) Regex

func (v *StringValidator) Regex(pattern string, message ...MessageArg) *StringValidator

Regex validates against pattern Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Required

func (v *StringValidator) Required(message ...MessageArg) *StringValidator

Required marks the field as required Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) RequiredIf

func (v *StringValidator) RequiredIf(fn func(data DataObject) bool) *StringValidator

RequiredIf makes field required based on condition

func (*StringValidator) RequiredUnless

func (v *StringValidator) RequiredUnless(fn func(data DataObject) bool) *StringValidator

RequiredUnless makes field required unless condition is met

func (*StringValidator) SameAs

func (v *StringValidator) SameAs(fieldPath string) *StringValidator

SameAs validates that this field equals another field's value

func (*StringValidator) StartsWith

func (v *StringValidator) StartsWith(prefix string, message ...MessageArg) *StringValidator

StartsWith validates string starts with prefix Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Transform

Transform adds a transformation function to be applied before validation

func (*StringValidator) Trim

func (v *StringValidator) Trim() *StringValidator

Trim trims whitespace before validation

func (*StringValidator) ULID

func (v *StringValidator) ULID(message ...MessageArg) *StringValidator

ULID validates ULID format (26 characters, Crockford base32) Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) URL

func (v *StringValidator) URL(message ...MessageArg) *StringValidator

URL validates URL format Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) URLWithOptions

func (v *StringValidator) URLWithOptions(opts UrlOptions) *StringValidator

URLWithOptions validates URL with specific scheme requirements

func (*StringValidator) UUID

func (v *StringValidator) UUID(message ...MessageArg) *StringValidator

UUID validates UUID format (v1-v5) Optionally accepts a custom error message (string or MessageFunc)

func (*StringValidator) Unique

func (v *StringValidator) Unique(table, column string, ignore any, wheres ...WhereClause) *StringValidator

Unique adds database uniqueness check

func (*StringValidator) Uppercase

func (v *StringValidator) Uppercase() *StringValidator

Uppercase converts to uppercase before validation

func (*StringValidator) Validate

func (v *StringValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type TimeValidator

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

TimeValidator validates time values with fluent API

func Time

func Time() *TimeValidator

Time creates a new time validator

func (*TimeValidator) After

func (v *TimeValidator) After(t time.Time) *TimeValidator

After validates time is after the specified time

func (*TimeValidator) AfterField

func (v *TimeValidator) AfterField(fieldPath string) *TimeValidator

AfterField validates time is after another field's time value

func (*TimeValidator) AfterNow

func (v *TimeValidator) AfterNow() *TimeValidator

AfterNow validates time is after current time

func (*TimeValidator) Before

func (v *TimeValidator) Before(t time.Time) *TimeValidator

Before validates time is before the specified time

func (*TimeValidator) BeforeField

func (v *TimeValidator) BeforeField(fieldPath string) *TimeValidator

BeforeField validates time is before another field's time value

func (*TimeValidator) BeforeNow

func (v *TimeValidator) BeforeNow() *TimeValidator

BeforeNow validates time is before current time

func (*TimeValidator) Between

func (v *TimeValidator) Between(start, end time.Time) *TimeValidator

Between validates time is between start and end (inclusive)

func (*TimeValidator) Custom

func (v *TimeValidator) Custom(fn func(value time.Time, lookup Lookup) error) *TimeValidator

Custom adds custom validation function

func (*TimeValidator) Default

func (v *TimeValidator) Default(value time.Time) *TimeValidator

Default sets default value if field is empty/missing

func (*TimeValidator) Format

func (v *TimeValidator) Format(format string) *TimeValidator

Format sets the expected time format (default: RFC3339)

func (*TimeValidator) Message

func (v *TimeValidator) Message(rule, message string) *TimeValidator

Message sets custom error message for a rule

func (*TimeValidator) Nullable

func (v *TimeValidator) Nullable() *TimeValidator

Nullable allows null values

func (*TimeValidator) Required

func (v *TimeValidator) Required() *TimeValidator

Required marks the field as required

func (*TimeValidator) RequiredIf

func (v *TimeValidator) RequiredIf(fn func(data DataObject) bool) *TimeValidator

RequiredIf makes field required based on condition

func (*TimeValidator) RequiredUnless

func (v *TimeValidator) RequiredUnless(fn func(data DataObject) bool) *TimeValidator

RequiredUnless makes field required unless condition is met

func (*TimeValidator) Timezone

func (v *TimeValidator) Timezone(loc *time.Location) *TimeValidator

Timezone sets the timezone for parsing

func (*TimeValidator) Validate

func (v *TimeValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type UnionValidator

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

UnionValidator validates value against multiple validators (any of)

func Union

func Union(validators ...Validator) *UnionValidator

Union creates a new union validator that accepts any of the provided validators

func (*UnionValidator) GetDBChecks

func (v *UnionValidator) GetDBChecks(fieldPath string, value any) []DBCheck

GetDBChecks returns database checks from all validators in the union

func (*UnionValidator) Message

func (v *UnionValidator) Message(rule, message string) *UnionValidator

Message sets custom error message for a rule

func (*UnionValidator) Nullable

func (v *UnionValidator) Nullable() *UnionValidator

Nullable allows null values

func (*UnionValidator) Required

func (v *UnionValidator) Required() *UnionValidator

Required marks the field as required

func (*UnionValidator) Validate

func (v *UnionValidator) Validate(ctx *ValidationContext, value any) map[string][]string

Validate implements Validator interface

type UniqueRule

type UniqueRule struct {
	Table   string
	Column  string
	Ignore  any // Value to ignore (for updates)
	Where   []WhereClause
	Message string
}

UniqueRule defines a database uniqueness check

type UrlOptions

type UrlOptions struct {
	Http  bool
	Https bool
}

UrlOptions for URL validation

type ValidationContext

type ValidationContext struct {
	Ctx      context.Context
	RootData DataObject
	Path     []string
	Options  *Options
}

ValidationContext holds validation state

func (*ValidationContext) FullPath

func (ctx *ValidationContext) FullPath() string

FullPath returns the dot-notation path string from the path slice

type ValidationError

type ValidationError struct {
	Errors map[string][]string
}

ValidationError holds all validation errors

func Parse

func Parse(data DataObject, schema Schema, opts ...Options) *ValidationError

Parse is an alias for Validate (Zod-like naming)

func Validate

func Validate(data DataObject, schema Schema, opts ...Options) *ValidationError

Validate validates data against a schema

func ValidateWithDB

func ValidateWithDB(ctx context.Context, data DataObject, schema Schema, checker DBChecker) *ValidationError

ValidateWithDB validates data with database checks using provided DBChecker

func (*ValidationError) Error

func (e *ValidationError) Error() string

func (*ValidationError) HasErrors

func (e *ValidationError) HasErrors() bool

type ValidationErrors

type ValidationErrors struct {
	Errors map[string][]string
}

ValidationErrors wraps multiple field errors

func (*ValidationErrors) Add

func (e *ValidationErrors) Add(field, message string)

Add adds an error for a field

func (*ValidationErrors) All

func (e *ValidationErrors) All() []string

All returns all errors as a flat slice

func (*ValidationErrors) Error

func (e *ValidationErrors) Error() string

func (*ValidationErrors) Fields

func (e *ValidationErrors) Fields() []string

Fields returns all field names with errors

func (*ValidationErrors) First

func (e *ValidationErrors) First(field string) string

First returns the first error for a field

func (*ValidationErrors) Get

func (e *ValidationErrors) Get(field string) []string

Get returns errors for a specific field

func (*ValidationErrors) HasErrors

func (e *ValidationErrors) HasErrors() bool

HasErrors returns true if there are any errors

type Validator

type Validator interface {
	Validate(ctx *ValidationContext, value any) map[string][]string
}

Validator is the interface all validators must implement

type WhereClause

type WhereClause struct {
	Column   string
	Operator string
	Value    any
}

WhereClause for DB queries

func Where

func Where(column, operator string, value any) WhereClause

Helper functions for where clauses

func WhereEq

func WhereEq(column string, value any) WhereClause

func WhereNot

func WhereNot(column string, value any) WhereClause

Jump to

Keyboard shortcuts

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