Skip to content

Repository files navigation

go.osspkg.com/validate

Go ReferenceGo Report CardLicense

validate is a lightweight, extensible validation library for Go with zero reflection overhead for callbacks, struct tagging support, and optional code generation for type-safe adapters.

Features

  • Rule‑based validation – register named rules with custom handlers.
  • Struct validation – use validate struct tags with support for required and multiple rules.
  • Callback‑based validation – validate multiple values in a single pass with Optional/Require.
  • Type‑safe adapters – generate boilerplate‑free adapters from your own functions using govld.
  • String decoding – automatically convert string inputs to most built‑in types and common interfaces.
  • Zero‑allocation pools – internal pooling for callback validators to reduce GC pressure.
  • Generics – used internally for caches and pools (Go 1.18+).

Installation

go get go.osspkg.com/validate

To use the code generation tool:

go install go.osspkg.com/validate/cmd/govld@latest

Quick Start

1. Register a rule and validate a struct

package main
import (
"context""fmt""go.osspkg.com/validate"
)
funcmain() {
v:=validate.New()
// Register a rule that checks if an int64 is greater than a reference_=v.Register(validate.Rule{
Name: "gt",
Handle: validate.HandlerFunc(func(ctx context.Context, valueany, opts...any) error {
val, ok:=value.(int64)
if!ok {
returnfmt.Errorf("expected int64, got %T", value)
}
iflen(opts) !=1 {
returnfmt.Errorf("expected 1 option")
}
ref, ok:=opts[0].(int64)
if!ok {
returnfmt.Errorf("option must be int64")
}
ifval<=ref {
returnfmt.Errorf("value %d must be greater than %d", val, ref)
}
returnnil
}),
})
typeUserstruct {
Ageint64`validate:"required;gt=18"`
}
u:=&User{Age: 25}
iferr:=v.ValidateStruct(context.Background(), u); err!=nil {
fmt.Println("Validation failed:", err)
} else {
fmt.Println("User is valid")
}
}

2. Callback‑based validation

funcvalidateUser(ctx context.Context, v*validate.Validator, userIDint64, namestring) error {
returnv.Validate(ctx, func(c validate.Callback) {
c.Require("gt", userID, int64(0)) // userID > 0c.Optional("nonempty", name) // only validated if name != ""
})
}

Core Concepts

Rule

A rule consists of a name (unique identifier) and a handler that implements validate.Handle:

typeHandleinterface {
ValidateHandle(ctx context.Context, valueany, opts...any) error
}

The validate.HandlerFunc type allows you to turn any function with the matching signature into a handler.

Struct Tags

Use the tag key validate. Multiple rules are separated by ;. The special required tag makes the field mandatory (zero values are not skipped).

Examples:

typeExamplestruct {
IDint`validate:"required;gt=0"`Namestring`validate:"nonempty;max=64"`Scorefloat64`validate:"min=-10;max=100"`
}

Rules can accept comma‑separated options:

`validate:"in=admin,moderator,user"`

Callback API

  • Require(name, value, opts...) – always runs the validation. Fails if the rule returns an error.
  • Optional(name, value, opts...) – only runs the validation when the value is not its zero value (see util.IsDefaultValue). Useful for partial updates.

Code Generation (govld)

Writing handlers manually with any type assertions is verbose. The govld tool generates type‑safe adapters from your own functions.

Step 1: Write a validation function

//go:generate govld -pkg .//govld:genfuncValidateUID(ctx context.Context, valueint64, minint64) error {
ifvalue<min {
returnfmt.Errorf("uid %d is less than minimum %d", value, min)
}
returnnil
}

The function must:

  • Have at least two parameters: context.Context and the value to validate.
  • Return only an error.
  • Be marked with the comment //govld:gen (exactly, no spaces).

Step 2: Run the generator

go generate ./...

This creates adapt_handlers_gen.go containing ValidateUIDAdaptHandler – a function that matches validate.HandlerFunc.

Step 3: Use the generated adapter

v.Register(validate.Rule{
Name: "uid",
Handle: validate.HandlerFunc(ValidateUIDAdaptHandler),
})

Now you can call the rule with proper types:

v.Validate(ctx, func(c validate.Callback) {
c.Require("uid", int64(123), int64(100))
})

The generated adapter automatically converts any values and string‑encoded options using validate.StringDecode.

String Decoding

The StringDecode function (used internally by adapters) converts a string into many Go types:

  • Basic types: string, []byte, int, uint, float, complex, bool
  • time.Duration, time.Time (RFC3339)
  • Interfaces: io.Writer, encoding.TextUnmarshaler, json.Unmarshaler, xml.Unmarshaler
  • Structs, maps, slices, arrays – via json.Unmarshal

You can use it directly:

varportintiferr:=validate.StringDecode(&port, "8080"); err!=nil {
// handle error
}

Benchmarks

Typical performance on a modern machine (Intel i9-12900KF):

Operationns/opallocs/opB/op
Validate (callback)~166348
ValidateStruct (simple tags)~118216
ValidateStruct with adapters~18817376

Full Example

package main
import (
"context""fmt""go.osspkg.com/validate"
)
//go:generate govld -pkg .//govld:genfuncpositiveInt(ctx context.Context, valueint, _any) error {
ifvalue<=0 {
returnfmt.Errorf("value must be positive")
}
returnnil
}
//govld:genfuncrangeCheck(ctx context.Context, valueint, min, maxint) error {
ifvalue<min||value>max {
returnfmt.Errorf("value %d out of range [%d,%d]", value, min, max)
}
returnnil
}
typeConfigstruct {
Portint`validate:"required;positiveInt"`Timeoutint`validate:"rangeCheck=100,5000"`
}
funcmain() {
v:=validate.New()
_=v.Register(
validate.Rule{Name: "positiveInt", Handle: validate.HandlerFunc(positiveIntAdaptHandler)},
validate.Rule{Name: "rangeCheck", Handle: validate.HandlerFunc(rangeCheckAdaptHandler)},
)
cfg:=&Config{Port: 8080, Timeout: 2000}
iferr:=v.ValidateStruct(context.Background(), cfg); err!=nil {
fmt.Println("Invalid config:", err)
} else {
fmt.Println("Config OK")
}
}

Run with:

go generate
go run .

License

BSD 3-Clause – see LICENSE file.

About

Custom validation of structures and parameters

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages