Documentation
¶
Overview ¶
Package vali is a tiny validation library.
It is pointer-insensitive, will always validate the value behind the pointer (i.e. *string required passes if string != "" not if *string != nil).
You can pass it a struct, a *struct, a *****struct, doesn't matter, it will always fast-forward to the value and ignore any pointers.
It is very small, but extensible, you can easily add your own checkers or "checker makers" (basically, checkers that can take arguments).
Index ¶
Examples ¶
Constants ¶
const DefaultValidatorTagName = "validate"
DefaultValidatorTagName holds the default struct tag name.
Variables ¶
var ( ErrCheckFailed = errors.New("check failed") ErrRequired = errors.New("value missing") ErrInvalidChecker = errors.New("invalid checker") ErrInvalidCmp = errors.New("invalid comparison") )
Possible errors.
var DefaultDontSkipZero = []string{"required", "eq", "ne", "min", "max"}
DefaultDontSkipZero holds the default list of checks that do NOT skip the zero value. By default, checks are skipping it, unless they are in this list.
This allows checks to be used for optional fields as well, i.e.: `validate:"uuid"` will allow an empty string and only validate it as uuid if not empty. To BOTH require it to be present and be an uuid, you would combine `validate:"required,uuid"`.
In short, checks should be kept small, focused and composable and avoid overlapping their responsibilities.
var DefaultValidator = New()
DefaultValidator allows using the library directly, without creating a validator, similar to how flags and net/http packages work.
Functions ¶
func Interface ¶ added in v1.5.0
Interface returns the value as an interface{}, working around the limitation that unexported fields cannot use reflect.Value.Interface().
This is useful when implementing custom checkers that need to work with both exported and unexported fields.
Returns nil if the value cannot be extracted (e.g., complex unexported types).
func RegisterChecker ¶
RegisterChecker registers a new Checker to the DefaultValidator.
func RegisterCheckerMaker ¶
func RegisterCheckerMaker(name string, fn CheckerMaker)
RegisterCheckerMaker registers a new CheckerMaker to the DefaultValidator.
func Validate ¶
Validate validates v against DefaultValidator. See Validator.Validate for details.
Types ¶
type Checker ¶
Checker repesents a basic checker (one that takes no arguments, i.e. "required").
func Eq ¶ added in v1.2.0
Eq checks numbers for being == `arg` and things with a `len()` (`array`, `chan`, `map`, `slice`, `string`) for having len == `arg`.
func Max ¶ added in v1.2.0
Max checks numbers for being at most `arg` and things with a `len()` (`array`, `chan`, `map`, `slice`, `string`) for having len at most `arg`.
func Min ¶ added in v1.2.0
Min checks numbers for being at least `arg` and things with a `len()` (`array`, `chan`, `map`, `slice`, `string`) for having len at least `arg`.
type CheckerMaker ¶
CheckerMaker is a way to construct checkers with arguments (i.e. "regex:^[A-Z]$").
type Validator ¶ added in v1.2.0
type Validator struct {
// Separator between checks (a), cheks and their arguments (b). The check between
// arguments themselves is not configurable (c), as that is ultimately up to each
// individual checker (how to parse the arguments). The only builtin check that uses
// it is `one_of` and that one requires it to be the pipe symbol.
//
// `validate:"required(a)uuid(a)one_of(b)foo|bar|baz"` which defaults to:
// `validate:"required,uuid,one_of:foo|bar|baz"`
CheckSep,
CheckArgSep string
// Checks in this list WILL be checked against the zero value.
// By default, checks are not run against the zero value, unless they
// are part of this list.
DontSkipZeroChecks []string
sync.RWMutex //nolint:embeddedstructfieldcheck // ok
// contains filtered or unexported fields
}
Validator holds the validation context. You can create your own or use the default one provided by this library.
func New ¶ added in v1.2.0
New creates a new Validator, initialized with the default checkers and ready to be used. You can optionally pass a struct tag name or use the DefaultValidatorTagName.
By default, it errors out if it encounters validation tags on private fields, but you can change that by setting the [Validator.ErrorOnPrivate] to false. The error will be of type [ErrPrivateField].
func (*Validator) RegisterChecker ¶ added in v1.2.0
func (*Validator) RegisterCheckerMaker ¶ added in v1.2.0
func (v *Validator) RegisterCheckerMaker(name string, fn CheckerMaker)
RegisterCheckerMaker registers a new CheckerMaker to the Validator.
func (*Validator) Validate ¶ added in v1.2.0
Validate validates a struct. The passed value v can be a value or a pointer (or pointer to a pointer, although there's no point to do that in Go). It will validate all the fields that have the `s.tag` present, recursively.
Example ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
func main() {
s := struct {
Foo struct {
Bar string `validate:"required"`
}
}{}
err := vali.Validate(s)
fmt.Println(err)
}
Output: Foo.Bar: required check failed: value missing
Example (Custom_checker) ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
func main() {
var phone string
s := struct {
Foo struct {
Bar *string `validate:"phone"`
}
}{}
s.Foo.Bar = &phone
p, err := vali.Regex(`^\d{3}-?\d{3}-?\d{4}$`)
if err != nil {
fmt.Println(err)
}
vali.RegisterChecker("phone", p)
phone = "123"
err = vali.Validate(s)
fmt.Println(err) // This should err.
phone = "123-456-7890"
err = vali.Validate(s)
fmt.Println(err) // This should not.
}
Output: Foo.Bar: phone check failed: "123" does not match ^\d{3}-?\d{3}-?\d{4}$ <nil>
Example (Custom_min) ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
func main() {
s := struct {
Foo struct {
Bar int8 `validate:"min10"`
}
}{}
min10, err := vali.Min("10")
if err != nil {
fmt.Println(err)
}
vali.RegisterChecker("min10", min10)
s.Foo.Bar = 9
err = vali.Validate(s)
fmt.Println(err) // This should err.
s.Foo.Bar = 10
err = vali.Validate(s)
fmt.Println(err) // This should not.
}
Output: Foo.Bar: min10 check failed: 9 is less than 10 <nil>
Example (Interface) ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
type foo struct{}
func (foo) Foo() string {
return "hello world"
}
func main() {
type fooer interface {
Foo() string
}
s := struct {
F fooer `validate:"required"`
}{}
err := vali.Validate(s)
fmt.Println(err) // This should err.
s.F = foo{}
err = vali.Validate(s)
fmt.Println(err) // This should not.
}
Output: F: required check failed: value missing <nil>
Example (Luhn) ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
func main() {
s := struct {
CreditCard string `validate:"luhn"`
}{}
// Valid credit card number (passing Luhn algorithm).
s.CreditCard = "4111 1111 1111 1111"
err := vali.Validate(s)
fmt.Println(err)
// Invalid credit card number (failing Luhn algorithm).
s.CreditCard = "4111 1111 1111 1112"
if err = vali.Validate(s); err != nil {
fmt.Println("Invalid")
}
}
Output: <nil> Invalid
Example (Ssn_npi) ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
func main() {
s := struct {
SSN string `validate:"ssn"`
NPI string `validate:"npi"`
}{}
// Valid SSN.
s.SSN = "123-45-6789"
err := vali.Validate(s)
fmt.Println("Valid SSN:", err)
// Invalid SSN format.
s.SSN = "12345-6789"
err = vali.Validate(s)
fmt.Println("Invalid SSN:", err != nil)
// Valid NPI (with 80840 prefix for Luhn check).
s.SSN = ""
s.NPI = "1234567893"
err = vali.Validate(s)
fmt.Println("Valid NPI:", err)
// Invalid NPI.
s.NPI = "12345"
err = vali.Validate(s)
fmt.Println("Invalid NPI:", err != nil)
}
Output: Valid SSN: <nil> Invalid SSN: true Valid NPI: <nil> Invalid NPI: true
Example (Unexported) ¶
package main
import (
"fmt"
"github.com/alexaandru/vali"
)
func main() {
s := struct {
Foo struct {
bar string `validate:"required,uuid"`
}
}{}
s.Foo.bar = "550e8400-e29b-41d4-a716-446655440000"
err := vali.Validate(s)
fmt.Println(err) // Private fields with tags are validated!
s.Foo.bar = "invalid"
err = vali.Validate(s)
fmt.Println(err) // Validation fails for invalid UUID.
}
Output: <nil> Foo.bar: uuid check failed: "invalid" does not match (?i)^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}$