A validator generator for structs/maps for your go applications.
- Simplicity
- Efficient validators are automatically generated(for ozzo-validation/v4)
- Prevent bugs by declaratively writing validator settings
- Generating a validator for the struct automatically generated by oapi-codegen
- Automatic generation of unit tests for validators
go install github.com/kanryu/validagen
e.g. There is a struct.
type Address struct {
Food string
Income float64
Mail string
People int
Street string
State string
}
You will write a toml as example.toml
.
# Type: struct or map
Type="struct"
# MethodName: (optional) method name of the validator. Validate is default
# Validators.XXX: Validator information for struct XXX
[Validators.Address]
# Name: (optional) struct identifier. key name is substituted as default
# Package: (optional) Validator package name. strings.ToLower(Name) as default
Package = "example"
# Receiver: (optional) receiver of the validator method
# Dir: (optional) Directory to output validator source code. Package name is default
# FileName: (optional) Validator file name. "[ToSnake(Name)]_validator.go" is the default
# FileMode: (optional) file mode of the validator source code 0644 is default
# Import: (optional) Add additional packages when validator source code imports them
# Properties: validators for each field of struct
Properties.Food.Type = "string"
Properties.Food.Required = true
Properties.Food.In = {String=["Cheeze", "Milk", "Meat"]}
Properties.Income.Type = "float"
Properties.Income.Required = true
Properties.Income.In = {Float=[1.1,2.2,3.3]}
Properties.Mail.Type = "string"
Properties.Mail.Required = true
Properties.Mail.Email = true
Properties.People.Type = "int"
Properties.People.Required = true
Properties.People.In = {Int=[1,2,3]}
Properties.State.Type = "string"
Properties.State.Required = true
Properties.State.match = "^[A-Z]{2}$"
Properties.Street.Type = "string"
Properties.Street.Required = true
Properties.Street.Length = [5,50]
You will run validagen.
validagen generate example.toml
You can find the validator as example/address_validator.go
// Code generated by github.com/kanryu/validagen. DO NOT EDIT.
package example
import (
"regexp"
"github.com/go-ozzo/ozzo-validation/v4/is"
validation "github.com/go-ozzo/ozzo-validation/v4"
)
// Validate validater for Address struct
func (a Address) Validate() error {
return validation.ValidateStruct(&a,
validation.Field(&a.Food, validation.In("Cheeze", "Milk", "Meat"), validation.Required),
validation.Field(&a.Income, validation.In(1.1, 2.2, 3.3), validation.Required),
validation.Field(&a.Mail, validation.Required, is.Email),
validation.Field(&a.People, validation.In(1, 2, 3), validation.Required),
validation.Field(&a.State, validation.Match(regexp.MustCompile("^[A-Z]{2}$")), validation.Required),
validation.Field(&a.Street, validation.Length(5,50), validation.Required),
)
}
You can get also test for the validators.
You will add lines into the toml.
[Validators.Address.TestData]
# Testing: flag of generating tests
Testing = true
# Valid: a map of valid data of the struct
Valid.Food = {String=["Cheeze"]}
Valid.Income = {Float=[2.2]}
Valid.Mail = {String=["[email protected]"]}
Valid.People = {Int=[3]}
Valid.State = {String=["SF"]}
Valid.Street = {String=["street"]}
# Invalid: amap of invalid data of the struct.
# each failed test generate contains on invalid property value
Invalid.Food = {String=["Cheezended"]}
Invalid.Income = {Float=[2.233]}
Invalid.Mail = {String=["nil"]}
Invalid.People = {Int=[33]}
Invalid.State = {String=["SFX"]}
Invalid.Street = {String=["fail"]}
After you run validagen again, you can get test as example/address_validator_test.go
.
package example
import (
"testing"
)
// TestAddress_Validate a test suite for (a *Address)Validate()
func TestAddress_Validate(t *testing.T) {
tests := []struct {
name string
a Address
wantErr bool
}{
{wantErr: false, name: "OK", a: Address{Food:"Cheeze",Income:2.2,Mail:"[email protected]",People:3,State:"SF",Street:"street"}},
{wantErr: true, name: "NG for Food", a: Address{Food:"Cheezended",Income:2.2,Mail:"[email protected]",People:3,State:"SF",Street:"street"}},
{wantErr: true, name: "NG for Income", a: Address{Food:"Cheeze",Income:2.233,Mail:"[email protected]",People:3,State:"SF",Street:"street"}},
{wantErr: true, name: "NG for Mail", a: Address{Food:"Cheeze",Income:2.2,Mail:"nil",People:3,State:"SF",Street:"street"}},
{wantErr: true, name: "NG for People", a: Address{Food:"Cheeze",Income:2.2,Mail:"[email protected]",People:33,State:"SF",Street:"street"}},
{wantErr: true, name: "NG for State", a: Address{Food:"Cheeze",Income:2.2,Mail:"[email protected]",People:3,State:"SFX",Street:"street"}},
{wantErr: true, name: "NG for Street", a: Address{Food:"Cheeze",Income:2.2,Mail:"[email protected]",People:3,State:"SF",Street:"fail"}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.a.Validate()
if tt.wantErr {
if err == nil {
t.Fatalf("%q. wantErr %v, but actual err %v", tt.name, tt.wantErr, err)
}
} else if err != nil {
t.Fatalf("%q. wantErr %v, but actual err occured %+v", tt.name, tt.wantErr, err)
}
})
}
}
KATO Kanryu([email protected])
MIT