Govalid
Struct validation using tags
Install / Use
/learn @twharmon/GovalidREADME
Govalid
Use Govalid to validate structs.
Basic Example
package main
import (
"errors"
"fmt"
"log"
"strings"
"unicode/utf8"
"github.com/twharmon/govalid"
)
type Post struct {
// ID has no constraints
ID int
// Title is required, must be at least 3 characters long, and
// cannot be more than 20 characters long
Title string `valid:"req|min:3|max:20"`
// Body is not required, cannot be more than 10000 charachers,
// and must be "fun" (a custom rule defined below).
Body string `valid:"max:10000|fun"`
}
func main() {
// Add custom string rule "fun" that can be used on any string field
// in any struct.
govalid.Rule("fun", func(v any) error {
switch tv := v.(type) {
case string:
if float64(strings.Count(tv, "!"))/float64(utf8.RuneCountInString(tv)) > 0.001 {
return nil
}
// return a validation error with govalid.Error
return govalid.NewValidationError("must contain more exclamation marks")
default:
// return a non validation (internal) error
return errors.New("fun constraint must be applied to string only")
}
})
fmt.Println(govalid.Validate(&Post{
ID: 5,
Title: "Hi",
Body: "Hello world!",
}))
}
Error Values
When you call govalid.Validate to validate a struct, it returns an error if the validation rules are not met. This error may either be a validation-specific error (an implementation of govalid.ValidationError) or a different error indicating a problem in processing the validation. This allows you to distinguish between errors caused by invalid data and those caused by issues in your validation logic, such as setting the valid tag to max:not-a-number.
if err := govalid.Validate(value); err != nil {
if verr, ok := err.(govalid.ValidationError); ok {
fmt.Println("validation error", verr)
} else {
fmt.Println("some other error", err)
}
}
Dive Usage
The dive rule is used to apply validation rules to elements within pointers, slices, arrays, and structs. When the dive rule is encountered, it instructs the validator to "dive" into the elements of the collection or the value pointed to by a pointer and apply the remaining rules to each element or the dereferenced value.
Notes
- Pointers: The
diverule will dereference the pointer and apply the remaining rules to the value it points to. - Slices/Arrays: The
diverule will iterate over each element in the slice or array and apply the remaining rules to each element. - Structs: The
diverule will validate the struct according to its own field tags. The remaining rules afterdivehave no meaning for structs.
Examples
Pointers
type Example struct {
Field *string `valid:"req|dive|min:3"`
}
In this example, the Field must be a non-nil pointer to a string, and the string must be at least 3 characters long.
Slices/Arrays
type Example struct {
Field []string `valid:"req|dive|min:3"`
}
In this example, the Field must be a non-nil slice of strings, and each string in the slice must be at least 3 characters long.
Structs
type Inner struct {
Name string `valid:"req"`
}
type Outer struct {
InnerStruct Inner `valid:"dive"`
}
In this example, the InnerStruct field will be validated according to the validation tags defined in the Inner struct.
In Rule
The in rule validates that a value is one of a specified set of values. Values are comma-separated and whitespace is trimmed. The rule is case-sensitive for strings.
type Example struct {
// Status must be one of: active, inactive, or pending
Status string `valid:"in:active,inactive,pending"`
// Code must be 1, 2, or 3
Code int `valid:"in:1,2,3"`
}
The in rule works with strings, all integer types (int, int8-64), and all unsigned integer types (uint, uint8-64).
Contribute
Make a pull request.
