Skip to content

Latest commit

 

History

History
78 lines (53 loc) · 5.97 KB

File metadata and controls

78 lines (53 loc) · 5.97 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Repository

github.com/go-playground/validator/v10 — struct and field validation library based on struct tags. The module path ends in /v10; changes must preserve v10 API compatibility. Contributions require test coverage (see .github/CONTRIBUTING.md).

Commands

make test              # go test -cover -race ./...
make lint              # installs golangci-lint v2.0.2 if missing, then runs it
make bench             # go test -run=NONE -bench=. -benchmem ./...

# Single test / subtest
go test -run TestName ./...
go test -run TestName/subtest_name ./...

# Single benchmark
go test -run=NONE -bench=BenchmarkFieldSuccess -benchmem ./...

Tests live alongside source in the package root (validator_test.go, benchmarks_test.go); run them from the repo root. Non-standard validators and translations are separate packages (./non-standard/validators, ./translations/...) and are included by ./....

Architecture

The library compiles validation tags into cached execution plans, then runs them against values via reflection. Understanding three layers is enough to be productive:

1. Registration & configuration — validator_instance.go

Validate is the singleton entry point. It holds:

  • validations — tag → FuncCtx (the validator functions)
  • aliases — shorthand tag → expanded tag expression
  • customFuncsreflect.Type → value extractor (for types like sql.NullString or anything implementing the Valuer interface, see doc.go)
  • structLevelFuncs — struct-level validators
  • tagCache and structCache — parsed-tag and parsed-struct caches (critical for performance; Validate is thread-safe and must be used as a singleton)

Options live in options.go (WithRequiredStructEnabled, WithPrivateFieldValidation, etc.). WithRequiredStructEnabled is the forward-compatible default users should adopt before v11.

2. Baked-in validators — baked_in.go

All built-in tags (required, email, uuid, oneof, gt, cross-field eqfield, etc.) are registered here as Func/FuncCtx receiving a FieldLevel (see field_level.go). restrictedTags enumerates names that cannot be overridden. Cross-field validators resolve the other field via fl.GetStructFieldOK* against the parent struct captured in the execution context.

Adjacent data tables:

When adding a new tag: register it in bakedInValidators in baked_in.go, add its description to the table in README.md, and add tests in validator_test.go.

3. Execution — validator.go + cache.go

  • cache.go parses struct tags into cField and cTag linked lists once per type and stores them in structCache/tagCache. cTag.typeof (typeDefault, typeOmitEmpty, typeDive, typeStructOnly, typeOr, etc.) tells the executor what to do.
  • validator.go contains the per-call validate struct (pooled via sync.Pool) and the validateStruct / traverseField mutual recursion. ns/actualNs are the accumulated dotted namespaces used in error paths; dive, keys, endkeys push/pop through slices/maps.

Public entry points live on Validate: Struct, StructCtx, StructPartial, StructExcept, StructFiltered, Var, VarWithValue, and their Ctx variants.

Errors

errors.go defines ValidationErrors (a []FieldError) and InvalidValidationError. Per README.md and doc.go, callers type-assert err.(validator.ValidationErrors) after checking err != nil. Only InvalidValidationError signals misuse (e.g. passing a non-struct to Struct).

Struct-level & field-level custom validators

struct_level.go and field_level.go define the contexts passed to user-registered functions. Struct-level validators report errors via StructLevel.ReportError / ReportValidationErrors — they operate on the whole struct instead of a single field.

Translations — translations.go + translations/

Each locale under translations/<locale> registers a human-readable message per tag against a ut.Translator. FieldError.Translate(trans) consumes the registered TranslationFunc. When adding a new tag with a translation, add an entry in each locale package — translations are parallel packages, not a single table.

Non-standard validators — non-standard/validators/

Opt-in validators (NotBlank) users register manually. Keep niche or opinionated validators here rather than expanding baked_in.go.

Performance-sensitive areas

Validation is on the hot path for many users. When touching cache.go, validator.go, or baked_in.go hot functions:

  • Avoid allocations in the success path — several baked-in validators and the executor reuse pooled buffers (validate.misc, str1, str2).
  • Benchmarks in benchmarks_test.go gate regressions; run make bench before and after significant changes and include results in the PR if they move.

Conventions

  • Go module minimum version is pinned in go.mod; don't lower it.
  • Don't introduce new top-level exported types without need — most extension happens through Register* methods on Validate.
  • restrictedTags (in baked_in.go) is intentionally a denylist for aliases and registrations; adding a tag with one of those names will break the parser.
  • Examples live under _examples/ (underscore prefix excludes them from the module build). Keep them runnable as standalone main packages.