Documentation
¶
Overview ¶
Package validate provides small, composable value validators.
A validator is just a func(T) error: it returns nil when the value is acceptable, or an error describing the problem. Nothing has to implement an interface, so a plain closure works anywhere a validator is expected. Combine validators with All (stop at the first failure) or Collect (run them all and join every failure).
username := validate.All(
validate.MinLength(3),
validate.MaxLength(16),
validate.Chars("abcdefghijklmnopqrstuvwxyz0123456789_"),
)
if err := username(input); err != nil {
// errors.Is(err, validate.ErrDisallowedChars) still works here
}
Errors are plain sentinel values (see Error); match them with errors.Is. Err matches any error produced by this package.
Comparison with other packages ¶
- go-playground/validator drives validation from struct tags and reflection.
- go-ozzo/ozzo-validation composes rules in code, but through a Rule interface over interface{}.
- asaskevich/govalidator is a bag of IsX string helpers.
This package is the code-composition style with two constraints kept deliberately: validators are ordinary generic functions, checked by the compiler with no reflection (In, Equal and the rest will not accept the wrong type), and there are no dependencies outside the standard library.
When to use something else ¶
Reach for go-playground/validator if you want struct-tag validation, translated messages, or a large catalogue of built-in checks. This package stays small and leaves struct/field wiring and error presentation to the caller.
Example ¶
A validator is any func(T) error. Compose the built-ins with your own closures using All (stop at the first failure) or Collect (report all).
package main
import (
"errors"
"fmt"
"strings"
"code.aneur.in/go/validate"
)
func main() {
username := validate.All(
validate.MinLength(3),
validate.MaxLength(16),
validate.Chars("abcdefghijklmnopqrstuvwxyz0123456789_"),
func(s string) error {
if strings.HasPrefix(s, "_") {
return errors.New("must not start with an underscore")
}
return nil
},
)
fmt.Println(username("ok_name"))
fmt.Println(username("_nope"))
fmt.Println(username("Nope"))
}
Output: <nil> must not start with an underscore contains disallowed characters
Example (CollectAll) ¶
Collect runs every validator and joins the failures; errors.Is still matches each one.
package main
import (
"errors"
"fmt"
"code.aneur.in/go/validate"
)
func main() {
password := validate.Collect(
validate.MinLength(8),
validate.Chars("abcdefghijklmnopqrstuvwxyz"),
)
err := password("Ab1")
fmt.Println(err)
fmt.Println(errors.Is(err, validate.ErrMustBeLonger))
fmt.Println(errors.Is(err, validate.ErrDisallowedChars))
}
Output: must contain at least 8 characters contains disallowed characters true true
Example (ErrorsIs) ¶
Errors are plain sentinels, so errors.Is works with no library-specific machinery. Err matches any error from this package.
package main
import (
"errors"
"fmt"
"code.aneur.in/go/validate"
)
func main() {
err := validate.Email("not-an-email")
fmt.Println(err)
fmt.Println(errors.Is(err, validate.ErrInvalidEmail))
fmt.Println(errors.Is(err, validate.Err))
}
Output: invalid email address true true
Index ¶
- Variables
- func All[T any](fs ...func(T) error) func(T) error
- func Chars(allow string) func(string) error
- func Collect[T any](fs ...func(T) error) func(T) error
- func Contains(substr string) func(string) error
- func Email(value string) error
- func Equal[T comparable](cmp T) func(T) error
- func ExceptChars(disallow string) func(string) error
- func In[T comparable](allow ...T) func(T) error
- func Match(re *regexp.Regexp) func(string) error
- func Max(n int, exclusive bool) func(int) error
- func MaxFloat32(n float32, exclusive bool) func(float32) error
- func MaxFloat64(n float64, exclusive bool) func(float64) error
- func MaxLength(l int) func(string) error
- func MaxLengthBytes(l int) func(string) error
- func MaxSize[T any](l int) func([]T) error
- func Min(n int, exclusive bool) func(int) error
- func MinFloat32(n float32, exclusive bool) func(float32) error
- func MinFloat64(n float64, exclusive bool) func(float64) error
- func MinLength(l int) func(string) error
- func MinLengthBytes(l int) func(string) error
- func MinSize[T any](l int) func([]T) error
- func NotIn[T comparable](disallow ...T) func(T) error
- func Prefix(prefix string) func(string) error
- func Required[T comparable]() func(T) error
- func Suffix(suffix string) func(string) error
- func URL(value string) error
- func UUID(value string) error
- type Error
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( ErrMustBeLonger = NewError("must contain at least %d characters") ErrMustBeShorter = NewError("must contain no more than %d characters") ErrMustHaveMoreBytes = NewError("must have at least %d bytes") ErrMustHaveFewerBytes = NewError("must have no more than %d bytes") )
var ( ErrMustBeGreater = NewError("must be greater than %v") ErrMustBeGreaterOrEqual = NewError("must be greater than or equal to %v") ErrMustBeLess = NewError("must be less than %v") ErrMustBeLessOrEqual = NewError("must be less than or equal to %v") )
var ( ErrMustHaveMoreItems = NewError("must have at least %d items") ErrMustHaveFewerItems = NewError("must have no more than %d items") )
var ( ErrMissingPrefix = NewError("must start with %q") ErrMissingSuffix = NewError("must end with %q") ErrMissingSubstring = NewError("must contain %q") )
var Err = Error{}
Err is the zero Error. It carries no message, so errors.Is(x, Err) is true for any error produced by this package.
var (
ErrDisallowedChars = NewError("contains disallowed characters")
)
var (
ErrInvalidEmail = NewError("invalid email address")
)
var (
ErrInvalidUUID = NewError("invalid UUID")
)
var (
ErrNoMatch = NewError("invalid format")
)
var (
ErrNotEqual = NewError("must be equal to %v")
)
var (
ErrRequired = NewError("is required")
)
var (
ErrValueNotAllowed = NewError("not allowed")
)
Functions ¶
func All ¶
All validates a value against a sequence of validation functions, stopping and returning the first error. See Collect to run every function and report all failures at once.
Example ¶
testAll := All(MinLength(4), Chars("0123456789abcdef"))
fmt.Println(testAll("invalid input"))
Output: contains disallowed characters
func Chars ¶
Chars validates whether a string contains only allowed characters.
Example ¶
testChars := Chars("0123456789abcdef")
fmt.Println(testChars("invalid input"))
Output: contains disallowed characters
func Collect ¶
Collect validates a value against a sequence of validation functions, like All, but runs every function instead of stopping at the first failure. All failures are returned together as one joined error (see errors.Join); errors.Is still matches each individual error contained in it. If nothing fails, Collect returns nil.
Example ¶
v := Collect(MinLength(4), Chars("0123456789abcdef"))
fmt.Println(v("xy"))
Output: must contain at least 4 characters contains disallowed characters
func Email ¶
Email validates an email address using net/mail.ParseAddress.
Only a bare address is accepted (alice@example.com). Anything with a display name, angle brackets, a comment, or trailing content is rejected, as is a list of addresses. Note that ParseAddress does not require the domain to have a dot, so "alice@localhost" is considered valid; layer on Match or a DNS check if you need to be stricter.
Example ¶
fmt.Println(Email("not an email"))
Output: invalid email address
func Equal ¶
func Equal[T comparable](cmp T) func(T) error
Equal validates whether an input value is equal to a comparison value.
func ExceptChars ¶
ExceptChars validates that a string contains none of the given characters.
Example ¶
testExceptChars := ExceptChars("0123456789abcdef")
fmt.Println(testExceptChars("invalid input"))
Output: contains disallowed characters
func In ¶
func In[T comparable](allow ...T) func(T) error
In validates that a value equals one of the allowed values.
Example ¶
testIn := In("abc", "def", "xyz")
fmt.Println(testIn("123"))
Output: not allowed
func Match ¶
Match validates that a string matches a regular expression. Compile the expression once with regexp.MustCompile and reuse the returned validator.
Example ¶
slug := Match(regexp.MustCompile(`^[a-z0-9-]+$`))
fmt.Println(slug("Not A Slug"))
Output: invalid format
func Max ¶
Max validates whether an integer is less than or equal to a given maximum. If exclusive is true, an equal value also produces an error.
Example ¶
testMax := Max(10, true) fmt.Println(testMax(10))
Output: must be less than 10
func MaxFloat32 ¶
MaxFloat32 validates whether a float32 is less than or equal to a given maximum. If exclusive is true, an equal value also produces an error.
func MaxFloat64 ¶
MaxFloat64 validates whether a float64 is less than or equal to a given maximum. If exclusive is true, an equal value also produces an error.
func MaxLength ¶
MaxLength validates that a string is no longer than a given maximum. Length is counted in runes, so multi-byte characters count as one.
Example ¶
testMaxLength := MaxLength(8)
fmt.Println(testMaxLength("this string is too long"))
Output: must contain no more than 8 characters
func MaxLengthBytes ¶
MaxLengthBytes validates that a string is no longer than a given maximum number of bytes (len). Prefer MaxLength for a limit on visible characters; use this when the budget is genuinely a byte count, such as a fixed-width column or a wire-format field.
Example ¶
testMaxLengthBytes := MaxLengthBytes(8)
fmt.Println(testMaxLengthBytes("cafés round the world"))
Output: must have no more than 8 bytes
func MaxSize ¶
MaxSize validates the length of a slice as being less than or equal to a given maximum.
Example ¶
testMaxSize := MaxSize[string](3)
fmt.Println(testMaxSize([]string{"abc", "def", "ghi", "jkl"}))
Output: must have no more than 3 items
func Min ¶
Min validates whether an integer is greater than or equal to a given minimum. If exclusive is true, an equal value also produces an error.
Example ¶
testMin := Min(10, false) fmt.Println(testMin(5))
Output: must be greater than or equal to 10
func MinFloat32 ¶
MinFloat32 validates whether a float32 is greater than or equal to a given minimum. If exclusive is true, an equal value also produces an error.
func MinFloat64 ¶
MinFloat64 validates whether a float64 is greater than or equal to a given minimum. If exclusive is true, an equal value also produces an error.
func MinLength ¶
MinLength validates that a string is at least a given minimum length. Length is counted in runes, so multi-byte characters count as one.
Example ¶
testMinLength := MinLength(8)
fmt.Println(testMinLength("2short"))
Output: must contain at least 8 characters
func MinLengthBytes ¶
MinLengthBytes validates that a string is at least a given minimum number of bytes (len). See MaxLengthBytes on when to prefer this over MinLength.
Example ¶
testMinLengthBytes := MinLengthBytes(8)
fmt.Println(testMinLengthBytes("2short"))
Output: must have at least 8 bytes
func MinSize ¶
MinSize validates the length of a slice as being greater than or equal to a given minimum.
Example ¶
testMaxSize := MinSize[string](3)
fmt.Println(testMaxSize([]string{"abc", "def"}))
Output: must have at least 3 items
func NotIn ¶
func NotIn[T comparable](disallow ...T) func(T) error
NotIn validates that a value equals none of the disallowed values.
func Prefix ¶
Prefix validates that a string begins with a given prefix.
Example ¶
fmt.Println(Prefix("https://")("http://example.com"))
Output: must start with "https://"
func Required ¶
func Required[T comparable]() func(T) error
Required validates that a value is not the zero value for its type: an empty string, 0, a nil pointer, and so on.
It is defined for comparable types only. For slices and maps, use MinSize(1); for "non-whitespace", combine with a trimming step.
Example ¶
notBlank := Required[string]()
fmt.Println(notBlank(""))
Output: is required
Types ¶
type Error ¶
Error represents a validation error.
func (Error) Error ¶
Error returns the error message. If Data is non-empty, Message is used as an fmt.Sprintf format string and Data as its arguments (this is how the sentinels with %d/%q verbs are filled in, e.g. via With).
func (Error) Is ¶
Is determines whether the Error is an instance of the target. https://pkg.go.dev/errors#Is
If the target is a validation error and specifies a message, this function returns true if the messages match. If the target is an empty validation error, this function always returns true.