Documentation
¶
Overview ¶
Package validator validates structs, maps, JSON documents, form values and single values against boolean rule expressions, and binds validated input into structs.
Inputs ¶
A Validator turns an input into a Validation. Struct reads rules from struct tags (validate:"..." by default); Map, JSON and Values take a field -> expression map; Value checks one value under the name "value". The package-level functions of the same names use a shared Validator with the built-in rules; construct your own with New when rules, messages or options differ. Constructors return input and rule errors immediately. Validate evaluates every field and collects failures; Valid reports only the verdict and stops at the first failure; ValidateAs validates and then binds the filtered input into a struct atomically.
Expressions ¶
An expression combines rules with !, && and ||, in that precedence order, and parentheses:
required && (email || regex:"^[a-z]+@example\\.com$")
Arguments follow a colon and are separated by commas (between:3,20); double quotes and backslashes protect commas, operators and parentheses. "dive" splits a collection field's expression into rules for the collection and rules for each element: "required && min:1 && dive && alpha". "sometimes" on the top-level && chain skips the field when it is absent from the input, which suits PATCH requests.
Rules and filters ¶
Most built-in rules pass empty values so that presence is the job of required, filled and notblank; WithStrictRequired makes required reject zero values as well. Size rules compare numbers by value and strings by rune count, unless a numeric or number assertion in the same expression switches them to numeric comparison. Rules returns the built-in catalog; custom rules implement Rule, or FallibleRule when the check itself can fail, and are registered with options. Filters (trim, lower, int, ...) transform a value before its rules run and before ValidateAs binds it.
Errors ¶
Validate returns *Errors for field failures, with messages resolved through WithMessages, WithTranslation, WithAttributes and AddMessages; AsErrors recovers the collection from a wrapped error. A rule that cannot complete yields a *RuleError wrapping ErrRuleEvaluation and is never reported as a field failure. Invalid expressions yield *RulesError wrapping ErrInvalidRules from the constructors and from Check. Bind and ValidateAs are atomic and report *BindError wrapping ErrBindConversion.
Concurrency ¶
A Validator is immutable and safe for concurrent use, and its expression, plan and struct caches are bounded. A Validation is not safe for concurrent use. Rules and filters must be safe for concurrent use, because one Validator serves many goroutines, and Passes must be deterministic, because the engine may evaluate a rule more than once for one value.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
type User struct {
Email string `validate:"required && email"`
Age int `validate:"required && gte:18"`
}
vd := validator.MustStruct(User{Email: "a@b.com", Age: 20})
_ = vd.Validate(context.Background())
fmt.Println("valid:", !vd.Fails())
}
Output: valid: true
Index ¶
- Variables
- func Check[T any]() error
- func IsEmpty(v any) bool
- func IsEmptyValue(rv reflect.Value) bool
- func Valid(ctx context.Context, data any) (bool, error)
- type BindError
- type Errors
- func (e *Errors) All() map[string]map[string]string
- func (e *Errors) Error() string
- func (e *Errors) Has(field string) bool
- func (e *Errors) Items() []FieldError
- func (e *Errors) Messages(field string) map[string]string
- func (e *Errors) One() string
- func (e *Errors) OneFor(field string) string
- func (e *Errors) String() string
- type FallibleRule
- type Field
- func (f *Field) Attrs() []string
- func (f *Field) Context() context.Context
- func (f *Field) Name() string
- func (f *Field) Reflect() reflect.Value
- func (f *Field) Root[T any]() (T, bool)
- func (f *Field) Sibling[T any](name string) (T, bool)
- func (f *Field) SiblingValue(name string) (reflect.Value, bool)
- func (f *Field) Value[T any]() (T, bool)
- type FieldError
- type FieldRules
- type Filter
- type Option
- func WithAttributes(attributes map[string]string) Option
- func WithFallibleRules(rules ...FallibleRule) Option
- func WithFilters(filters ...Filter) Option
- func WithMessages(messages map[string]string) Option
- func WithParallel(minFields int) Option
- func WithPrivateFieldValidation() Option
- func WithRuleFunc(signature string, fn func(*Field) bool, message string) Option
- func WithRules(rules ...Rule) Option
- func WithStrictRequired() Option
- func WithStringRule(signature string, fn func(string, ...string) bool, message string) Option
- func WithTagName(name string) Option
- func WithTagNameFunc(fn TagNameFunc) Option
- func WithTransformFunc(fn TransformFunc) Option
- func WithTranslation(messages map[string]string) Option
- func WithTranslator(fn TranslatorFunc) Option
- func WithoutBuiltinRules() Option
- type Rule
- type RuleError
- type RuleInfo
- type RulesError
- type TagNameFunc
- type TransformFunc
- type TranslatorFunc
- type Validation
- func JSON[Bytes ~[]byte | ~string](data Bytes, rules map[string]string) (*Validation, error)
- func Map[M ~map[K]V, K comparable, V any](data M, rules map[string]string) (*Validation, error)
- func MustJSON[Bytes ~[]byte | ~string](data Bytes, rules map[string]string) *Validation
- func MustMap[M ~map[K]V, K comparable, V any](data M, rules map[string]string) *Validation
- func MustStruct(data any) *Validation
- func MustValue(value any, rule string) *Validation
- func MustValues(data url.Values, rules map[string]string) *Validation
- func Struct(data any) (*Validation, error)
- func Value(value any, rule string) (*Validation, error)
- func Values(data url.Values, rules map[string]string) (*Validation, error)
- func (vd *Validation) AddFilters(field string, filters ...string) error
- func (vd *Validation) AddMessages(messages map[string]string) error
- func (vd *Validation) AddRules(field string, rules ...string) error
- func (vd *Validation) Bind[T any](dst *T) error
- func (vd *Validation) ClearFilters(field string) error
- func (vd *Validation) ClearRules(field string) error
- func (vd *Validation) Err() error
- func (vd *Validation) Errors() *Errors
- func (vd *Validation) Fails() bool
- func (vd *Validation) Filters() map[string]string
- func (vd *Validation) RemoveFilters(field string, filters ...string) error
- func (vd *Validation) RemoveRules(field string, rules ...string) error
- func (vd *Validation) Rules() map[string]string
- func (vd *Validation) Validate(ctx context.Context) error
- func (vd *Validation) ValidateAs[T any](ctx context.Context, dst *T) error
- type Validator
- func (v *Validator) Check[T any]() error
- func (v *Validator) CheckType(t reflect.Type) error
- func (v *Validator) Describe[T any]() ([]FieldRules, error)
- func (v *Validator) DescribeType(t reflect.Type) ([]FieldRules, error)
- func (v *Validator) JSON[Bytes ~[]byte | ~string](data Bytes, rules map[string]string) (*Validation, error)
- func (v *Validator) Map[M ~map[K]V, K comparable, V any](data M, rules map[string]string) (*Validation, error)
- func (v *Validator) MustJSON[Bytes ~[]byte | ~string](data Bytes, rules map[string]string) *Validation
- func (v *Validator) MustMap[M ~map[K]V, K comparable, V any](data M, rules map[string]string) *Validation
- func (v *Validator) MustStruct(data any) *Validation
- func (v *Validator) MustValue(value any, rule string) *Validation
- func (v *Validator) MustValues(data url.Values, rules map[string]string) *Validation
- func (v *Validator) Struct(data any) (*Validation, error)
- func (v *Validator) Valid(ctx context.Context, data any) (bool, error)
- func (v *Validator) Value(value any, rule string) (*Validation, error)
- func (v *Validator) Values(data url.Values, rules map[string]string) (*Validation, error)
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidOption reports a nil Option or an invalid option argument: an // empty tag name, a nil function or map, a nil rule or filter, or a // non-positive WithParallel threshold. ErrInvalidOption = errors.New("validator: invalid option") // ErrInvalidSignature reports a rule or filter signature that is empty, // reserved ("dive") or not made of letters, digits and underscores // starting with a non-digit. ErrInvalidSignature = errors.New("validator: invalid signature") // ErrDuplicateRule reports a Rule or FallibleRule signature registered // twice, including a custom rule that shadows a built-in one. ErrDuplicateRule = errors.New("validator: duplicate rule") // ErrDuplicateFilter reports a Filter signature registered twice. ErrDuplicateFilter = errors.New("validator: duplicate filter") )
Configuration errors. New wraps them with the offending option, signature or rule named, so match them with errors.Is.
var ( // ErrNilContext reports a nil context passed to Validate, ValidateAs or // Valid. The call does not count as a validation run. ErrNilContext = errors.New("validator: nil context") // ErrInvalidInput reports input a constructor cannot use: a non-struct // value for Struct or Valid, or JSON that is malformed, has trailing data // or is not an object. ErrInvalidInput = errors.New("validator: invalid input") // ErrInvalidRules reports a rule expression that does not parse, names an // unknown rule, or has bad static arguments. It is wrapped by RulesError, // which names the field. ErrInvalidRules = errors.New("validator: invalid rules") // ErrRuleEvaluation reports a rule that could not complete: a FallibleRule // returned an error or a rule panicked. It is wrapped by RuleError and is // never recorded as a field failure. ErrRuleEvaluation = errors.New("validator: rule evaluation failed") // ErrRulePanic reports a panic recovered from a rule, a filter's caller or // reflection over the input; RuleError wraps it together with // ErrRuleEvaluation. ErrRulePanic = errors.New("validator: rule panicked") // ErrValidated reports a configuration change (AddRules, AddFilters, // AddMessages and their Remove/Clear forms) attempted after Validate ran. ErrValidated = errors.New("validator: validation already ran") )
Input, rule and runtime errors returned by the constructors, Validate, ValidateAs and Valid. They are wrapped, so match them with errors.Is.
var ( // ErrBindTarget reports a Bind or ValidateAs destination that is nil or // whose type parameter is not a non-pointer struct. ErrBindTarget = errors.New("validator: bind requires a non-nil pointer to a non-pointer struct") // ErrBindConversion reports a value that cannot be converted to its // destination field: integer overflow, an unparsable string, nil into a // non-nilable kind, or an incompatible shape. BindError wraps it and // names the field. ErrBindConversion = errors.New("validator: bind conversion failed") // ErrValidationFailed marks a ValidateAs error caused by field failures, // as opposed to a rule execution or binding error; the joined error also // carries the *Errors collection for AsErrors. ErrValidationFailed = errors.New("validator: validation failed") )
Binding errors returned by Bind and ValidateAs.
Functions ¶
func Check ¶ added in v0.4.2
Check compiles every rule tag reachable from struct type T with the Default Validator and reports the invalid ones; see Validator.Check.
Example ¶
package main
import (
"errors"
"fmt"
"github.com/libtnb/validator"
)
func main() {
type Signup struct {
Email string `validate:"required && emial"` // typo
}
err := validator.Check[Signup]()
fmt.Println(errors.Is(err, validator.ErrInvalidRules))
var rulesErr *validator.RulesError
if errors.As(err, &rulesErr) {
fmt.Println(rulesErr.Field)
}
}
Output: true Email
func IsEmpty ¶
IsEmpty reports whether v is absent or the empty value of its kind, the omitempty semantics the built-in rules share: nil, "", 0, false, an empty string, collection or channel, a nil pointer or func, and a zero time.Time. A non-time struct is never empty, so its rules still run. Custom rules use it to let empty values through and leave presence to required.
func IsEmptyValue ¶
IsEmptyValue is IsEmpty for a reflect.Value such as the result of Field.Reflect or Field.SiblingValue; an invalid Value is empty.
func Valid ¶
Valid reports whether data passes its struct-tag rules using the Default Validator; see Validator.Valid for the verdict and error semantics.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
type User struct {
Name string `validate:"required && alpha"`
}
ok, err := validator.Valid(context.Background(), User{Name: "alice"})
fmt.Println(ok, err)
}
Output: true <nil>
Types ¶
type BindError ¶ added in v0.4.2
type BindError struct {
// Field is the validation name of the destination field.
Field string
// Target is the Go type the value should have been converted to.
Target reflect.Type
// Value is the input value that failed to convert.
Value any
// Err is the cause, ErrBindConversion.
Err error
}
BindError identifies the first field that Bind or ValidateAs could not convert into the destination struct. Binding is atomic, so the destination is unchanged when this error is returned. Unwrap yields Err.
type Errors ¶
type Errors struct {
// contains filtered or unexported fields
}
Errors is the read-only collection of field failures produced by a Validation. Messages are resolved on every read from the Validator's messages, translations and attributes plus the Validation's AddMessages overrides, so the raw templates are kept and the same collection can be rendered under another configuration. It implements error: Validate and Err return it when fields failed, possibly joined with a *RuleError, and AsErrors recovers it from a wrapped error.
func AsErrors ¶ added in v0.2.0
AsErrors extracts the *Errors collection from an error returned by Validate, Err or ValidateAs, looking through errors.Join and fmt.Errorf wrappers. ok is false for errors that carry no field failures: rule execution errors, binding errors, a nil context or cancellation.
func (*Errors) All ¶
All returns every failure as field -> rule -> message, keeping the first message when a rule failed more than once on a field. The map is freshly built and safe to modify.
func (*Errors) Error ¶ added in v0.4.2
Error joins the first message of each failed field as "field: message", sorted by field name and separated by "; ". It is "" when nothing failed, which only happens on an Errors obtained before Validate.
func (*Errors) Items ¶ added in v0.2.0
func (e *Errors) Items() []FieldError
Items returns copies of the recorded failures in the order they were recorded (filter failures first, then fields by name, each container before its dive elements), with Message resolved and Params cloned. The slice is never nil.
func (*Errors) Messages ¶
Messages returns the message of each rule that failed on field, keyed by rule signature ("!" for a negation, "||" for an alternation with no passing branch, "" for a failed filter). When a rule failed more than once the first message is kept. The map is freshly built and safe to modify.
func (*Errors) One ¶
One returns the first recorded message, or "" when nothing failed. Use it when a single line, such as an HTTP 400 body, is enough.
type FallibleRule ¶ added in v0.4.2
type FallibleRule interface {
// Signature returns the name used in expressions, under the same rules as
// Rule.Signature; one name cannot be registered as both kinds.
Signature() string
// Validate reports whether the value is acceptable, or an error when the
// check itself failed. It must not retain f after returning.
Validate(f *Field) (bool, error)
// Message returns the failure template used when Validate returns false
// with a nil error.
Message() string
}
FallibleRule is a rule backed by an operation that can fail, such as a database lookup. The Rule contract applies (concurrency-safe, deterministic for one input, pooled Field not retained), and false with a nil error is an ordinary field failure. A non-nil error means the check could not be completed: it is returned as a *RuleError wrapping ErrRuleEvaluation, is never recorded as a field failure, and skips the rest of that field's expression while other fields still run. Use f.Context to bound the operation.
type Field ¶
type Field struct {
// contains filtered or unexported fields
}
Field is the value and request context handed to a Rule, FallibleRule or rule function. Fields are pooled: one is valid only during the call that receives it, so never retain a *Field or the slice returned by Attrs. A Field is not safe for concurrent use.
func (*Field) Attrs ¶
Attrs returns the rule's arguments as written in the expression, unquoted and split on commas; raw-argument rules such as regex receive exactly one. The slice is shared and read-only and is valid only during the call.
func (*Field) Context ¶
Context returns the context passed to Validate, ValidateAs or Valid, so a FallibleRule can bound its I/O. Outside a validation run it is context.Background.
func (*Field) Name ¶
Name returns the validation name a failure is reported under: the Go or tag-derived name, dotted for nested struct fields, "field[i]" or "field[key]" for a dive element, and "value" for Value inputs.
func (*Field) Reflect ¶ added in v0.4.2
Reflect returns the current value with pointers and interfaces unwrapped and filters applied. An absent, nil or explicitly null value is an invalid reflect.Value, so check IsValid before calling methods on it. Under WithPrivateFieldValidation a value read from an unexported field may not be interfaceable.
func (*Field) Root ¶ added in v0.4.2
Root returns the whole input as T: the struct value (never its pointer) for Struct inputs, map[string]any for Map, JSON and Values inputs, and the value as given for Value inputs. ok is false when T does not match.
func (*Field) Sibling ¶
Sibling resolves another field like SiblingValue and returns it as T; ok is false when the name is unknown or the value has another dynamic type.
func (*Field) SiblingValue ¶ added in v0.4.2
SiblingValue resolves another field's current value for cross-field rules. A bare name is looked up among the current field's siblings first (the same nested struct or map object; for a dive element, the container's siblings), then from the root; a dotted name is absolute. The relative step never resolves to the field itself. ok is false when the name is unknown; Value inputs have no siblings.
type FieldError ¶
type FieldError struct {
// Field is the validation name; dive elements are reported as "field[i]"
// for slices and arrays and "field[key]" for maps.
Field string
// Rule is the failing rule's signature, "!" for a negation, "||" for an
// alternation with no passing branch, or "" for a failed filter.
Rule string
// Message is the raw template with {field}, {0}, {1}, ... and {N+}
// placeholders; Errors resolves it, honoring overrides and translations.
Message string
// Params are the rule's arguments, substituted for {0}, {1}, ...; {N+}
// joins the arguments from N on with ", ".
Params []string
}
FieldError is one recorded failure: a rule or filter that rejected a field. Errors resolves Message through the Validator's templates on every read; Errors.Items returns copies with the final text filled in.
type FieldRules ¶ added in v0.4.0
type FieldRules struct {
// Name is the field's validation name, dotted for nested fields.
Name string
// Index locates the field for reflect.Type.FieldByIndex.
Index []int
// Rules apply to the field value itself.
Rules []RuleInfo
// Element rules apply to slice/array/map elements (dive).
Element []RuleInfo
// Exact is false when || or ! branches were omitted.
Exact bool
}
FieldRules lists the rules declared on one struct field, flattened from the top-level AND chain of its expression. Element holds the rules that apply to container elements (after dive). Rules under || or ! cannot be flattened losslessly: those subtrees are omitted and Exact reports false, so a consumer knows the list is a lower bound.
func Describe ¶ added in v0.4.2
func Describe[T any]() ([]FieldRules, error)
Describe reports the rules declared on struct type T using the Default Validator; see Validator.Describe.
Example ¶
package main
import (
"fmt"
"github.com/libtnb/validator"
)
func main() {
type User struct {
Name string `validate:"required && min:3"`
Tags []string `validate:"dive && alpha"`
}
fields, _ := validator.Describe[User]()
for _, f := range fields {
fmt.Println(f.Name, f.Rules, f.Element)
}
}
Output: Name [{required []} {min [3]}] [] Tags [] [{alpha []}]
type Filter ¶
type Filter interface {
// Signature returns the name used in filter chains, under the same rules
// as Rule.Signature.
Signature() string
// Handle returns the transformed value; args are the chain arguments
// ("trim:xy" passes "xy"). A value that cannot be transformed should be
// an error rather than a silent zero.
Handle(val any, args ...string) (any, error)
}
Filter transforms an input value before its rules see it and before ValidateAs binds it: trimming, case folding, type coercion. A field's chain ("trim|lower") runs once at Validate, in order, only on a present non-nil value, and never on a field with dive rules. A returned error, or a panic (recovered), is recorded as a field failure whose message is the error text; the raw value is never used silently. Implementations must be safe for concurrent use.
type Option ¶
type Option func(*config) error
Option configures a Validator during New. Options run in the order given and the first error aborts construction; the resulting Validator is immutable.
func WithAttributes ¶
WithAttributes maps validation names to the display names substituted for {field} in messages. Later options merge over earlier ones. A nil map is ErrInvalidOption.
func WithFallibleRules ¶ added in v0.4.2
func WithFallibleRules(rules ...FallibleRule) Option
WithFallibleRules registers FallibleRule implementations, whose errors surface as *RuleError instead of field failures. The same nil, signature and duplicate checks as WithRules apply.
func WithFilters ¶ added in v0.4.2
WithFilters registers custom Filter implementations. A nil filter is ErrInvalidOption, a malformed signature is ErrInvalidSignature, and a signature already registered is ErrDuplicateFilter at New.
func WithMessages ¶
WithMessages overrides failure templates, keyed by "field.rule" or "rule"; the more specific key wins. Templates use {field}, {0}, {1}, ... and {N+} (the arguments from N on, comma-joined). Later options merge over earlier ones. A nil map is ErrInvalidOption.
func WithParallel ¶
WithParallel evaluates fields concurrently, on up to GOMAXPROCS goroutines, once a validation has at least minFields rule-bearing fields. Rules and filters must then be safe for concurrent use. Evaluation is sequential when the option is omitted; minFields <= 0 is ErrInvalidOption.
func WithPrivateFieldValidation ¶
func WithPrivateFieldValidation() Option
WithPrivateFieldValidation includes unexported struct fields in plans, so their tags are validated and they resolve as siblings and bind targets. Their values are read through unsafe access to an addressable copy of the input.
func WithRuleFunc ¶ added in v0.4.2
WithRuleFunc registers a Rule built from a function, for checks that need no state. fn is bound by the Rule contract: deterministic, safe for concurrent use, and not retaining the Field. message is the failure template. A nil fn is ErrInvalidOption; signature is checked like WithRules.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
v := validator.MustNew(validator.WithRuleFunc(
"even",
func(f *validator.Field) bool {
n, ok := f.Value[int]()
return ok && n%2 == 0
},
"The {field} must be even.",
))
vd := v.MustValue(3, "required && even")
_ = vd.Validate(context.Background())
fmt.Println(vd.Errors().One())
}
Output: The value must be even.
func WithRules ¶ added in v0.4.2
WithRules registers custom Rule implementations. A nil rule is ErrInvalidOption, a malformed signature is ErrInvalidSignature, and a signature already registered (built-in or custom) is ErrDuplicateRule at New.
func WithStrictRequired ¶
func WithStrictRequired() Option
WithStrictRequired makes required and the required_* family reject zero values (0, "", false, empty collections, nil, zero time.Time) as well as absent ones. Without it required only checks presence, so a submitted empty string passes; use filled or notblank when that must fail.
func WithStringRule ¶ added in v0.4.2
WithStringRule registers a Rule over the string form of the value: empty values pass (IsEmpty semantics) and other values are rendered with conv.ToString before fn sees them together with the rule's arguments. A nil fn is ErrInvalidOption; signature is checked like WithRules.
func WithTagName ¶
WithTagName selects the struct tag that holds rule expressions; the default is "validate". An empty name is ErrInvalidOption.
func WithTagNameFunc ¶
func WithTagNameFunc(fn TagNameFunc) Option
WithTagNameFunc installs a TagNameFunc, typically to name fields after their json tag so error keys match the request body. A nil fn is ErrInvalidOption.
func WithTransformFunc ¶
func WithTransformFunc(fn TransformFunc) Option
WithTransformFunc installs a TransformFunc applied to every resolved message. A nil fn is ErrInvalidOption.
func WithTranslation ¶
WithTranslation sets localized templates with the same keys and placeholders as WithMessages but lower precedence, so a WithMessages entry still wins. The translations package provides ready-made maps. A nil map is ErrInvalidOption.
func WithTranslator ¶
func WithTranslator(fn TranslatorFunc) Option
WithTranslator installs a TranslatorFunc consulted after the message and translation maps, for catalogs that cannot be materialized as a map. A nil fn is ErrInvalidOption.
func WithoutBuiltinRules ¶
func WithoutBuiltinRules() Option
WithoutBuiltinRules starts from an empty registry, so only the rules and filters added by other options exist and an expression naming a built-in fails to compile. Rules and Filters expose the catalog for re-registering a subset.
type Rule ¶
type Rule interface {
// Signature returns the name used in expressions: letters, digits and
// underscores, not starting with a digit, and not the reserved "dive".
Signature() string
// Passes reports whether the field's current value is acceptable. It
// must not retain f or the slice returned by f.Attrs after returning.
Passes(f *Field) bool
// Message returns the failure template; {field} is the display name and
// {0}, {1}, ... the arguments. An empty template renders as
// "The {field} is invalid.".
Message() string
}
Rule is a leaf boolean check; composition with &&, || and ! is the expression's job. A Validator is shared between goroutines, so implementations must be safe for concurrent use. Passes must be deterministic and side-effect free: the engine evaluates a rule more than once for one value (a fast probe before diagnostics on dive elements, Valid versus Validate), so a rule that answers differently on the second call produces inconsistent reports. The *Field is pooled and only valid during the call. A check that performs I/O or can fail implements FallibleRule instead.
By convention empty values pass (IsEmpty), leaving presence to required, filled and notblank, so rules compose without repeating presence checks.
type RuleError ¶ added in v0.4.2
type RuleError struct {
// Field is the validation name of the field being evaluated, or "" when
// the failure cannot be attributed to one field.
Field string
// Rule is the signature of the FallibleRule that returned the error; ""
// for a recovered panic.
Rule string
// Err is the cause: the FallibleRule's error, or ErrRulePanic wrapped with
// the panic value. It is nil only in a zero RuleError.
Err error
}
RuleError identifies a rule whose evaluation could not be completed: a FallibleRule returned a non-nil error, or a rule or the reflection reading the input panicked. It is an operational failure, not a verdict on the data, so it is never added to Errors: Validate and Valid return it (joined with any field failures), and ValidateAs returns it without ErrValidationFailed. The failing field's remaining rules are skipped; other fields are still evaluated.
Unwrap yields ErrRuleEvaluation joined with Err, so errors.Is matches the sentinel as well as the cause (ErrRulePanic for a recovered panic, or the FallibleRule's own error).
type RuleInfo ¶ added in v0.4.0
type RuleInfo struct {
// Name is the rule signature as written in the expression.
Name string
// Args are the unquoted arguments, nil when none were given.
Args []string
}
RuleInfo is one rule invocation from an expression: min:3 becomes Name "min" with Args ["3"]. Args are unquoted; a rule written without arguments has nil Args.
type RulesError ¶ added in v0.4.2
type RulesError struct {
// Field is the validation name whose expression failed to compile.
Field string
// Err is the cause, typically a parse error carrying the position.
Err error
}
RulesError identifies a field whose rule expression is invalid: a parse error, an unknown rule, bad static arguments (a regex that does not compile, a missing sibling name, an unparsable file size), or "sometimes" placed under ||, ! or dive. The constructors, Check and CheckType return one RulesError per bad field, joined with errors.Join; nothing is evaluated.
Unwrap yields ErrInvalidRules joined with Err, so errors.Is matches both.
func (*RulesError) Error ¶ added in v0.4.2
func (e *RulesError) Error() string
Error renders the field and cause; a nil receiver or a nil Err reports invalid rules.
func (*RulesError) Unwrap ¶ added in v0.4.2
func (e *RulesError) Unwrap() error
Unwrap returns ErrInvalidRules joined with Err, or ErrInvalidRules alone when Err is nil, so errors.Is sees both.
type TagNameFunc ¶
type TagNameFunc func(field reflect.StructField) string
TagNameFunc derives a field's validation name from its struct field, for example from a json tag so error keys match the wire format; returning "" keeps the Go field name. The name is used in error keys, messages, attributes and cross-field references.
type TransformFunc ¶
TransformFunc post-processes every resolved message, for example to capitalize or wrap it. A panic inside it is recovered and the untransformed message is used.
type TranslatorFunc ¶
TranslatorFunc looks up a template by rule signature at message resolution time, after the WithMessages and WithTranslation maps miss; returning false falls through to the rule's own Message. It must be safe for concurrent use.
type Validation ¶
type Validation struct {
// contains filtered or unexported fields
}
Validation is one input together with its rule configuration and, after Validate, its result. Constructors return it with the input's rules attached; AddRules, AddFilters and AddMessages may adjust it until Validate runs, after which they return ErrValidated. Validate runs once and the result is read through Errors, Err and Fails.
A Validation is not safe for concurrent use and is meant to serve one request; the Validator behind it is the shareable object.
func JSON ¶
JSON prepares a Validation from one JSON object and explicit field expressions with the Default Validator; see Validator.JSON.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
vd := validator.MustJSON(`{"name": "alice", "age": 17}`, map[string]string{
"name": "required && alpha",
"age": "required && gte:18",
})
err := vd.Validate(context.Background())
fmt.Println(err)
}
Output: age: The age field must be greater than or equal to 18.
func Map ¶
func Map[M ~map[K]V, K comparable, V any](data M, rules map[string]string) (*Validation, error)
Map prepares a Validation for a map and explicit field expressions with the Default Validator; see Validator.Map.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
vd := validator.MustMap(
map[string]any{"name": "alice", "role": "root"},
map[string]string{
"name": "required && alpha",
"role": "required && in:admin,user",
},
)
_ = vd.Validate(context.Background())
fmt.Println(vd.Errors().OneFor("role"))
}
Output: The selected role is invalid.
func MustJSON ¶ added in v0.4.2
func MustJSON[Bytes ~[]byte | ~string](data Bytes, rules map[string]string) *Validation
MustJSON is JSON with panic-on-error semantics.
func MustMap ¶ added in v0.4.2
func MustMap[M ~map[K]V, K comparable, V any](data M, rules map[string]string) *Validation
MustMap is Map with panic-on-error semantics.
func MustStruct ¶ added in v0.4.2
func MustStruct(data any) *Validation
MustStruct is Struct with panic-on-error semantics.
func MustValue ¶ added in v0.4.2
func MustValue(value any, rule string) *Validation
MustValue is Value with panic-on-error semantics.
func MustValues ¶ added in v0.4.2
func MustValues(data url.Values, rules map[string]string) *Validation
MustValues is Values with panic-on-error semantics.
func Struct ¶
func Struct(data any) (*Validation, error)
Struct prepares a struct-tag validation with the Default Validator; see Validator.Struct for the input rules and errors.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
type User struct {
Email string `validate:"required && email"`
Age int `validate:"required && gte:18"`
}
vd, err := validator.Struct(User{Email: "not-an-email", Age: 17})
if err != nil {
panic(err) // not a struct, or a tag that does not compile
}
err = vd.Validate(context.Background())
errs, _ := validator.AsErrors(err)
fmt.Println(errs.OneFor("Email"))
fmt.Println(errs.OneFor("Age"))
}
Output: The Email must be a valid email address. The Age field must be greater than or equal to 18.
func Value ¶ added in v0.4.2
func Value(value any, rule string) (*Validation, error)
Value prepares a Validation for one value under the field name "value" with the Default Validator; see Validator.Value.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
vd := validator.MustValue("nope", "required && email")
_ = vd.Validate(context.Background())
fmt.Println(vd.Fails())
}
Output: true
func Values ¶ added in v0.4.2
Values prepares a Validation from form or query data with the Default Validator; a repeated key becomes a []string. See Validator.Values.
Example ¶
package main
import (
"context"
"fmt"
"net/url"
"reflect"
"strings"
"github.com/libtnb/validator"
)
func main() {
type Form struct {
Name string `json:"name"`
Tags []string `json:"tags"`
}
// Name fields after their json tag so form keys and struct fields match.
v := validator.MustNew(validator.WithTagNameFunc(func(f reflect.StructField) string {
name, _, _ := strings.Cut(f.Tag.Get("json"), ",")
return name
}))
// tags=go&tags=web: a repeated key becomes a []string, so dive checks each value.
form := url.Values{"name": {"alice"}, "tags": {"go", "web"}}
vd := v.MustValues(form, map[string]string{
"name": "required && alpha",
"tags": "required && dive && alpha",
})
var f Form
if err := vd.ValidateAs(context.Background(), &f); err != nil {
fmt.Println(err)
return
}
fmt.Println(f.Name, f.Tags)
}
Output: alice [go web]
func (*Validation) AddFilters ¶
func (vd *Validation) AddFilters(field string, filters ...string) error
AddFilters appends filters to field's chain. Each string is a "|"-separated chain of names with optional ":"-arguments ("trim", "trim:xy", "int"); "\" escapes a separator. The whole chain is checked and an unknown filter returns an error leaving the chain unchanged. Filters run at Validate, before rules, and their output is what ValidateAs binds; a field with dive rules is never filtered, because a scalar filter would stringify the collection. Empty strings are skipped.
Returns ErrValidated after Validate has run.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
type Form struct {
Email string
}
vd := validator.MustMap(
map[string]any{"Email": " Alice@Example.com "},
map[string]string{"Email": "required && email"},
)
// Filters run before the rules; ValidateAs binds the filtered value.
_ = vd.AddFilters("Email", "trim|lower")
var f Form
if err := vd.ValidateAs(context.Background(), &f); err != nil {
fmt.Println(err)
return
}
fmt.Println(f.Email)
}
Output: alice@example.com
func (*Validation) AddMessages ¶ added in v0.1.1
func (vd *Validation) AddMessages(messages map[string]string) error
AddMessages overrides failure templates for this Validation only, keyed by "field.rule" or "rule" as in WithMessages, and takes precedence over the Validator's messages and translations. Overrides also apply to failures already recorded, since messages are resolved on read. An empty map is a no-op.
Returns ErrValidated after Validate has run.
func (*Validation) AddRules ¶
func (vd *Validation) AddRules(field string, rules ...string) error
AddRules appends expressions to field's rules, joined with &&. A top-level || on either side is parenthesized so precedence is preserved, and when the existing expression dives, the new rules apply to the container, before dive. Empty strings are skipped. The candidate is compiled first: an invalid expression returns its parse error and leaves the field's rules unchanged. A field unknown to the input is simply validated as absent.
Returns ErrValidated after Validate has run.
func (*Validation) Bind ¶
func (vd *Validation) Bind[T any](dst *T) error
Bind writes the raw input into dst without validating. Every field of T that the input names is converted (strings to numbers, booleans and times, []any to typed slices, nested objects to structs), untagged fields included; fields the input does not name keep their value. Binding is atomic: on any failure dst is left exactly as it was. T must be a non-pointer struct type.
Returns ErrBindTarget for a nil dst or a non-struct T, and *BindError (wrapping ErrBindConversion) naming the first field that could not be converted.
func (*Validation) ClearFilters ¶
func (vd *Validation) ClearFilters(field string) error
ClearFilters drops field's entire filter chain; an unknown field is a no-op.
Returns ErrValidated after Validate has run.
func (*Validation) ClearRules ¶
func (vd *Validation) ClearRules(field string) error
ClearRules drops field's entire expression, dive rules included; an unknown field is a no-op.
Returns ErrValidated after Validate has run.
func (*Validation) Err ¶
func (vd *Validation) Err() error
Err returns the result of Validate as an error: nil when the input passed (a literal nil, never a typed nil), *Errors when fields failed, *RuleError or the cancellation cause when the run could not complete, and the two joined when both apply. Before Validate it is nil.
func (*Validation) Errors ¶
func (vd *Validation) Errors() *Errors
Errors returns the collection of field failures. It is empty before Validate and after a passing run. The pointer stays valid for the life of the Validation and its messages reflect AddMessages overrides.
func (*Validation) Fails ¶
func (vd *Validation) Fails() bool
Fails reports whether Validate recorded field failures or could not complete. It is false before Validate.
func (*Validation) Filters ¶
func (vd *Validation) Filters() map[string]string
Filters returns a copy of the current field -> chain map. The map is never nil and modifying it has no effect on the Validation.
func (*Validation) RemoveFilters ¶
func (vd *Validation) RemoveFilters(field string, filters ...string) error
RemoveFilters drops the named filters from field's chain, matching either the name ("trim") or the full segment ("trim:xy"). An unknown field or no names is a no-op; a chain that becomes empty loses its entry.
Returns ErrValidated after Validate has run.
func (*Validation) RemoveRules ¶
func (vd *Validation) RemoveRules(field string, rules ...string) error
RemoveRules deletes the named rules from the top-level && chain of field's expression; a name may also be a full segment such as "min:3". Rules nested under ||, ! or parentheses, and "dive" itself, are never removed. An unknown field or no names is a no-op; a field whose expression becomes empty loses its entry.
Returns ErrValidated after Validate has run.
func (*Validation) Rules ¶
func (vd *Validation) Rules() map[string]string
Rules returns a copy of the current field -> expression map, reflecting AddRules, RemoveRules and ClearRules. The map is never nil and modifying it has no effect on the Validation.
func (*Validation) Validate ¶
func (vd *Validation) Validate(ctx context.Context) error
Validate runs filters and rules and records the result. The first call with a non-nil context freezes the Validation: later calls, and Err, return the same result, including one caused by a context that was already done. Fields are evaluated in name order (concurrently above the WithParallel threshold), a "sometimes" field is skipped when absent, and every field and every && operand is evaluated so Errors is complete.
Returns ErrNilContext for a nil ctx, the context's cancellation cause when ctx was done before or during evaluation, *Errors when fields failed, and *RuleError when a rule could not complete; the last two may be joined. A nil error means the input passed.
func (*Validation) ValidateAs ¶ added in v0.4.2
func (vd *Validation) ValidateAs[T any](ctx context.Context, dst *T) error
ValidateAs runs Validate and, on success, binds the filtered input into dst like Bind, so trimmed or coerced values are what the caller receives. Nothing is written unless validation and every conversion succeed.
Returns ErrBindTarget for a nil dst or a non-struct T (checked before validating); ErrNilContext for a nil ctx; *RuleError or the context's cancellation cause when validation could not complete; *Errors joined with ErrValidationFailed when fields failed, so AsErrors recovers the details; and *BindError when a conversion fails. Binding errors are not field failures and carry no ErrValidationFailed.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
type User struct {
Email string `validate:"required && email"`
Age int `validate:"required && numeric"`
}
vd := validator.MustMap(
map[string]any{"Email": "a@b.com", "Age": "42"},
map[string]string{"Email": "required && email", "Age": "required && numeric"},
)
_ = vd.Validate(context.Background())
var u User
if err := vd.ValidateAs(context.Background(), &u); err == nil {
fmt.Printf("%s / %d\n", u.Email, u.Age)
}
}
Output: a@b.com / 42
type Validator ¶
type Validator struct {
// contains filtered or unexported fields
}
Validator compiles rule expressions and produces Validations. It is immutable after New and safe for concurrent use, so share one per configuration (rules, filters, messages, options) rather than constructing one per request. Compiled expressions, struct plans and rules-map plans are cached inside it: the first validation of a type or rule set pays the compilation cost, later ones do not.
func Default ¶
func Default() *Validator
Default returns the shared Validator behind the package-level functions: the built-in rules and filters with default options, created on first use and safe for concurrent use. Construct your own Validator with New when rules, messages, translations or other options differ.
func MustNew ¶ added in v0.4.2
MustNew is New for static configuration: it panics with the error New would return.
func New ¶ added in v0.4.2
New constructs a Validator from options, applied in order. The built-in rules and filters are registered first unless WithoutBuiltinRules is given, so a custom rule named like a built-in one fails with ErrDuplicateRule.
Returns ErrInvalidOption for a nil option or an invalid option argument, ErrInvalidSignature for a malformed rule or filter name, and ErrDuplicateRule or ErrDuplicateFilter for a signature registered twice.
func (*Validator) Check ¶ added in v0.4.2
Check compiles every rule expression reachable from struct type T, nested and embedded fields included, and reports each invalid tag as a *RulesError joined into one error. Call it at start-up so a typo in a tag fails there rather than on the first request. T must be a non-pointer struct type; any other type is an error.
func (*Validator) CheckType ¶ added in v0.4.2
CheckType is Check for a reflect.Type, for schema generators and other callers that discover types at runtime. Pointer types are dereferenced; a nil or non-struct type is an error.
func (*Validator) Describe ¶ added in v0.4.2
func (v *Validator) Describe[T any]() ([]FieldRules, error)
Describe reports the rules declared on struct type T for consumers that translate them into another representation, such as an OpenAPI generator mapping min:3 to minLength. Every field with a non-empty tag yields one FieldRules, in plan order; struct-valued and embedded fields with tags are included because their rules are enforced too. Unknown rule names are reported as written rather than rejected, so pair it with Check to catch typos. T must be a non-pointer struct type.
Returns an error when T is not a struct type or an expression does not parse; the message carries the position.
func (*Validator) DescribeType ¶ added in v0.4.2
func (v *Validator) DescribeType(t reflect.Type) ([]FieldRules, error)
DescribeType is Describe for a reflect.Type, for schema generators that discover nested types dynamically. Pointer types are dereferenced; a nil or non-struct type is an error.
func (*Validator) JSON ¶
func (v *Validator) JSON[Bytes ~[]byte | ~string]( data Bytes, rules map[string]string, ) (*Validation, error)
JSON decodes one JSON object and prepares a Validation with explicit field expressions, as Map does. Numbers are decoded as int64, uint64 or float64 rather than float64 alone, so large integers keep their precision. The document must be a single object: duplicate names, invalid UTF-8, trailing data and non-object roots are rejected.
Returns ErrInvalidInput for malformed or non-object input and *RulesError values (wrapping ErrInvalidRules) for expressions that do not compile.
func (*Validator) Map ¶
func (v *Validator) Map[M ~map[K]V, K comparable, V any]( data M, rules map[string]string, ) (*Validation, error)
Map prepares a Validation over any map type with explicit field expressions (field name -> expression). Non-string keys are rendered with conv.ToString, and when two keys render the same the survivor is chosen deterministically. Dotted expression names reach into nested maps. The rules map is compiled once and cached by content; it is neither retained nor modified, so it may be a package-level literal.
Returns *RulesError values (wrapping ErrInvalidRules) for expressions that do not compile.
Example ¶
package main
import (
"context"
"fmt"
"github.com/libtnb/validator"
)
func main() {
v := validator.MustNew()
vd := v.MustMap(
map[string]any{"name": "alice"},
// boolean DSL: AND-grouped OR
map[string]string{"name": "required && (alpha || in:bob,carol)"},
)
_ = vd.Validate(context.Background())
fmt.Println("valid:", !vd.Fails())
}
Output: valid: true
func (*Validator) MustJSON ¶ added in v0.4.2
func (v *Validator) MustJSON[Bytes ~[]byte | ~string](data Bytes, rules map[string]string) *Validation
MustJSON is JSON with panic-on-error semantics.
func (*Validator) MustMap ¶ added in v0.4.2
func (v *Validator) MustMap[M ~map[K]V, K comparable, V any](data M, rules map[string]string) *Validation
MustMap is Map with panic-on-error semantics.
func (*Validator) MustStruct ¶ added in v0.4.2
func (v *Validator) MustStruct(data any) *Validation
MustStruct is Struct with panic-on-error semantics, for inputs whose type and tags are known to be valid.
func (*Validator) MustValue ¶ added in v0.4.2
func (v *Validator) MustValue(value any, rule string) *Validation
MustValue is Value with panic-on-error semantics.
func (*Validator) MustValues ¶ added in v0.4.2
MustValues is Values with panic-on-error semantics.
func (*Validator) Struct ¶
func (v *Validator) Struct(data any) (*Validation, error)
Struct prepares a Validation whose rules come from data's struct tags. data is a struct or a pointer to one; a nil pointer validates as the zero value, so required fields still fail. Fields are named by their Go name (or by WithTagNameFunc), nested structs use dotted names, embedded structs promote their fields, and a "-" tag excludes a field and its nested fields from validation while keeping them bindable. The type's plan is built once and cached. Nothing is evaluated until Validate.
Returns ErrInvalidInput when data is not a struct, and *RulesError values (wrapping ErrInvalidRules) for tags that do not compile.
func (*Validator) Valid ¶
Valid reports whether data passes its struct-tag rules without building an error collection: it stops at the first failing field and, for built-in rules, allocates nothing, so prefer it when only the verdict matters. It never applies filters. The error is non-nil only when validation could not complete (ErrNilContext, ErrInvalidInput, *RulesError, *RuleError or the context's cancellation cause); false with a nil error is a plain failure.
func (*Validator) Value ¶ added in v0.4.2
func (v *Validator) Value(value any, rule string) (*Validation, error)
Value prepares a Validation for one value under the field name "value", for ad-hoc checks such as a path parameter or a configuration setting. The expression is compiled once and cached. Cross-field rules find no siblings here.
Returns *RulesError (wrapping ErrInvalidRules) when rule does not compile.
func (*Validator) Values ¶ added in v0.4.2
Values prepares a Validation from form or query data with explicit field expressions. A key with one value maps to that string and a key with several values (tags=a&tags=b) maps to a []string, so repeated parameters validate with dive and bind into slice fields. Every value is a string: use numeric, number or boolean rules, or filters such as int, to interpret them.
Returns *RulesError values (wrapping ErrInvalidRules) for expressions that do not compile.
Source Files
¶
- bind.go
- cache.go
- collection.go
- comparison.go
- compile.go
- crossfield.go
- defaults.go
- doc.go
- errors.go
- eval.go
- field.go
- file.go
- filter.go
- filters.go
- format.go
- helpers.go
- introspect.go
- message.go
- numeric.go
- option.go
- presence.go
- reflect.go
- registry.go
- ruleexpr.go
- rules.go
- scope.go
- source.go
- string.go
- struct.go
- time.go
- types.go
- validation.go
- validator.go
Directories
¶
| Path | Synopsis |
|---|---|
|
Command examples is a runnable validator demo; the leading-underscore dir is skipped by the go tool, so run it directly: go run ./_examples
|
Command examples is a runnable validator demo; the leading-underscore dir is skipped by the go tool, so run it directly: go run ./_examples |
|
contrib
|
|
|
gormrules
module
|
|
|
openapi
module
|
|
|
Package conv converts loosely typed input (form strings, decoded JSON, named scalar types) into Go values without silent data loss: overflow, non-finite floats and unparsable strings return errors wrapping ErrConvert, and float-to-integer conversion truncates toward zero.
|
Package conv converts loosely typed input (form strings, decoded JSON, named scalar types) into Go values without silent data loss: overflow, non-finite floats and unparsable strings return errors wrapping ErrConvert, and float-to-integer conversion truncates toward zero. |
|
internal
|
|
|
dsl
Package dsl is the rule-expression engine behind the validator: a lexer, a parser with precedence ! > && > ||, the AST the compiler consumes, and the token-level helpers (dive splitting, top-level leaf removal, top-level || detection) that let the validator edit expressions without re-parsing them as text.
|
Package dsl is the rule-expression engine behind the validator: a lexer, a parser with precedence ! > && > ||, the AST the compiler consumes, and the token-level helpers (dive splitting, top-level leaf removal, top-level || detection) that let the validator edit expressions without re-parsing them as text. |
|
Package is provides pure syntax predicates for common string formats: network addresses, identifiers, encodings and coordinates.
|
Package is provides pure syntax predicates for common string formats: network addresses, identifiers, encodings and coordinates. |
|
Package translations provides localized message templates for the validator's built-in rules, keyed by rule signature, for use with validator.WithTranslation.
|
Package translations provides localized message templates for the validator's built-in rules, keyed by rule signature, for use with validator.WithTranslation. |