MCPcopy Index your code
hub / github.com/SladeThe/yav

github.com/SladeThe/yav @v0.16.3

Chat with this repo
repository ↗ · DeepWiki ↗ · release v0.16.3 ↗ · + Follow
2,116 symbols 5,029 edges 152 files 25 documented · 1%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Yet Another Validator

Go struct and field validation.

The project is inspired by go-playground/validator and uses some of its codebase. \ YAV aims to provide Playground-like validator configuration and produce compatible errors whenever possible. \ At the same time, the introduced chained validator improves validation speed dramatically. \ The Playground validator's performance is quite poor due to heavy reflection usage, which YAV strives not to use.

YAV's key principles: - mimic Playground validator whenever possible; - make fewer to zero allocations; - work fast.

The main drawback of other builder-like validators (e.g. ozzo) is that they are interface{}-based and allocate a lot of memory upon each run. Sometimes, it even makes them slower than the Playground validator.

Unlike in earlier versions, the repo no longer includes Playground validator wrapper in order to reduce the number of 3rd-party dependencies. The removed code still can be found in yav-tests.

Examples

Single field struct validation

The field name passed to yav.Chain doesn't affect the validation process and is only necessary for building a validation error, so that you may use whatever naming style you like, i.e. id, ID, Id.

type AccountID struct {
    ID string
}

func (id AccountID) Validate() error {
    return yav.Chain(
        "id", id.ID,
        vstring.Required,
        vstring.UUID,
    )
}

Combine validation errors

Use yav.Join to combine multiple validation errors.

type Password struct {
    Salt, Hash []byte
}

func (p Password) Validate() error {
    return yav.Join(
        yav.Chain(
            "salt", p.Salt,
            vbytes.Required,
            vbytes.Max(200),
        ),
        yav.Chain(
            "hash", p.Hash,
            vbytes.Required,
            vbytes.Max(200),
        ),
    )
}

Validate nested structs

Use yav.Nested to add value namespace, i.e. to get password.salt error instead of just salt. \ Contrary, any possible id error is returned as if the field were in the Account struct directly.

type Account struct {
    AccountID

    Login    string
    Password Password
}

func (a Account) Validate() error {
    return yav.Join(
        a.AccountID.Validate(),
        yav.Chain(
            "login", a.Login,
            vstring.Required,
            vstring.Between(4, 20),
            vstring.Alphanumeric,
            vstring.Lowercase,
        ),
        yav.Nested("password", a.Password.Validate()),
    )
}

Compare YAV and Playground validator

Here we pass to YAV Go-like field names in order to produce Playground-compatible errors. \ YAV doesn't anyhow use it, except while building validation errors. \ If compatibility is not required, pass the field names in whatever style you prefer.

type Account struct {
    ID string `validate:"required,uuid"`

    Login    string `validate:"required,min=4,max=20,alphanum,lowercase"`
    Password string `validate:"required_with=Login,omitempty,min=8,max=32,text"`

    Email string `validate:"required,min=6,max=100,email"`
    Phone string `validate:"required,min=8,max=16,e164"`
}

func (a Account) Validate() error {
    return yav.Join(
        yav.Chain(
            "ID", a.ID,
            vstring.Required,
            vstring.UUID,
        ),
        yav.Chain(
            "Login", a.Login,
            vstring.Required,
            vstring.Min(4),
            vstring.Max(20),
            vstring.Alphanumeric,
            vstring.Lowercase,
        ),
        yav.Chain(
            "Password", a.Password,
            vstring.RequiredWithAny().String(a.Login).Names("Login"),
            vstring.Between(8, 32),
            vstring.Text,
        ),
        yav.Chain(
            "Email", a.Email,
            vstring.Required,
            vstring.Between(6, 100),
            vstring.Email,
        ),
        yav.Chain(
            "Phone", a.Phone,
            vstring.Required,
            vstring.Between(8, 16),
            vstring.E164,
        ),
    )
}

Available validations

Common

OmitEmpty
Required
RequiredIf
RequiredUnless
RequiredWithAny
RequiredWithoutAny
RequiredWithAll
RequiredWithoutAll
ExcludedIf
ExcludedUnless
ExcludedWithAny
ExcludedWithoutAny
ExcludedWithAll
ExcludedWithoutAll

Bool

Equal
NotEqual

Bytes

Min
Max
Between

Duration

Min
Max
Between
LessThan
LessThanOrEqual
GreaterThan
GreaterThanOrEqual

LessThanNamed
LessThanOrEqualNamed
GreaterThanNamed
GreaterThanOrEqualNamed

Map

Min
Max
Between

Unique

Keys
Values

Number

Min
Max
Between
LessThan
LessThanOrEqual
GreaterThan
GreaterThanOrEqual

Equal
NotEqual
OneOf

Slice

Min
Max
Between

Unique

Items

String

Min
Max
Between

Equal
NotEqual
OneOf

Alpha
Alphanumeric
Lowercase
Uppercase
ContainsAlpha
ContainsLowerAlpha
ContainsUpperAlpha
ContainsDigit
ContainsSpecialCharacter
ExcludesWhitespace
StartsWithAlpha
StartsWithLowerAlpha
StartsWithUpperAlpha
StartsWithDigit
StartsWithSpecialCharacter
EndsWithAlpha
EndsWithLowerAlpha
EndsWithUpperAlpha
EndsWithDigit
EndsWithSpecialCharacter

Text
Title

E164
Email
Hostname
HostnameRFC1123
HostnamePort
FQDN
URI
URL
UUID

Regexp

Time

Min
Max
Between
LessThan
LessThanOrEqual
GreaterThan
GreaterThanOrEqual

LessThanNamed
LessThanOrEqualNamed
GreaterThanNamed
GreaterThanOrEqualNamed

Benchmarks

goos: windows
goarch: amd64
cpu: Intel(R) Core(TM) i9-10850K CPU @ 3.60GHz

Tiny struct validation

BenchmarkYAV              12907930       92.15 ns/op          0 B/op        0 allocs/op
BenchmarkOzzo              1334562       890.1 ns/op       1248 B/op       20 allocs/op
BenchmarkPlayground        1324868       911.8 ns/op         40 B/op        2 allocs/op

Account struct validation

BenchmarkYAV                729123        1658 ns/op        123 B/op        4 allocs/op
BenchmarkPreAllocatedYAV    802777        1488 ns/op          0 B/op        0 allocs/op
BenchmarkOzzo*               54954       21684 ns/op      19215 B/op      317 allocs/op
BenchmarkPlayground         172633        6789 ns/op        653 B/op       23 allocs/op

Notes

  • The Account in the Examples section is a reduced version of the benchmarked structure.
  • Ozzo validator lacks some features available in both YAV and Playground validator. Therefore, those validation steps were not enabled for ozzo.
  • The YAV is still slower, than a manually written validation boilerplate, but the amount of code differs dramatically.

Extension points exported contracts — how you extend this code

Validatable (Interface)
(no doc) [7 implementers]
yav.go
ProvideFunc (FuncType)
(no doc)
accumulators/provide.go
ValidateFunc (FuncType)
ValidateFunc usually represents a single validation check against the given named value.
yav.go
Zeroer (Interface)
(no doc) [1 implementers]
yav.go

Core symbols most depended-on inside this repo

RegisterMapEntry
called by 199
internal/internal.go
Names
called by 186
accumulators/required_with_any.go
Int
called by 174
accumulators/required_with_any.go
String
called by 52
accumulators/required_with_any.go
IsZero
called by 31
yav.go
ErrRequired
called by 23
errors.go
NewExcludedWithoutAll
called by 21
accumulators/excluded_without_all.go
NewRequiredWithAny
called by 21
accumulators/required_with_any.go

Shape

Function 1,380
Method 368
Struct 247
TypeAlias 117
FuncType 2
Interface 2

Languages

Go100%

Modules by API surface

vnumber/range.go252 symbols
vnumber/range_test.go86 symbols
vnumber/equal.go60 symbols
vnumber/equal_test.go38 symbols
vnumber/required_without_any.go36 symbols
vnumber/required_without_all.go36 symbols
vnumber/required_with_any.go36 symbols
vnumber/required_with_all.go36 symbols
vnumber/required_unless.go36 symbols
vnumber/required_if.go36 symbols
vnumber/excluded_without_any.go36 symbols
vnumber/excluded_without_all.go36 symbols

For agents

$ claude mcp add yav \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact