Skip to content

Commit

Permalink
Merge pull request #19 from superstas/aliases_validation
Browse files Browse the repository at this point in the history
Added ability of generation validators for aliases by in-place tags; …
  • Loading branch information
v1-wizard authored Oct 3, 2017
2 parents a395bda + b8a0dce commit 8ea5e7f
Show file tree
Hide file tree
Showing 37 changed files with 1,001 additions and 96 deletions.
31 changes: 30 additions & 1 deletion entities.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ type FieldDef struct {
fieldType types.TypeDef
}

func NewField(fieldNames types.FieldTagsNames, fieldType types.TypeDef, validateTags []types.ValidatableTag) (*FieldDef, error) {
func NewField(fieldNames types.FieldTagsNames, fieldType types.TypeDef, validateTags types.ValidatableTags) (*FieldDef, error) {
fieldType = toPrimitiveType(fieldType, validateTags)
for _, t := range validateTags {
if err := fieldType.SetValidateTag(t); err != nil {
return nil, fmt.Errorf("set validateTags failed, field %s, tag: %+v, err: %s",
Expand All @@ -125,3 +126,31 @@ func NewField(fieldNames types.FieldTagsNames, fieldType types.TypeDef, validate
fieldType: fieldType,
}, nil
}

func toPrimitiveType(fieldType types.TypeDef, validateTags types.ValidatableTags) types.TypeDef {
if validateTags.Empty() {
return fieldType
}

if len(validateTags) == 1 &&
(validateTags.ContainsTag(types.SimpleTag{Name: types.PointerNotNullKey})) ||
(validateTags.ContainsTag(types.SimpleTag{Name: types.StructFuncKey})) {
return fieldType
}

if fieldType.Expr() == nil {
return fieldType
}

underlyingPrimitive := parsePrimitiveType(fieldType.Expr(), fmt.Sprintf("field type %s", fieldType.Type()))
if underlyingPrimitive != nil {
// case: pointer to custom type with not_null: ( "Name *SimpleStringType `validate:not_null,min_len=1`")
p, isPtr := fieldType.(*types.TypePointer)
if isPtr {
return p.SetInnerType(underlyingPrimitive)
}
return underlyingPrimitive
}

return fieldType
}
40 changes: 40 additions & 0 deletions examples/aliases/entities.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package aliases

import (
"errors"
"unicode/utf8"
)

// StringType validator will be overridden by validation tags in-place
type StringType string

func (t StringType) Validate() error {
if utf8.RuneCountInString(string(t)) < 3 {
return errors.New("shorter than 3 chars")
}
if utf8.RuneCountInString(string(t)) > 64 {
return errors.New("longer than 64 chars")
}
return nil
}

func (t StringType) ValidateNotEmpty() error {
if t == "" {
return errors.New("string is empty")
}
return nil
}

type IntType int
type FloatType float64
type MapType map[string]int

type User struct {
FirstName StringType `validate:"min_len=2,max_len=15"`
LastName string `validate:"min_len=1,max_len=15"`
NonEmptyString StringType `validate:"func=.ValidateNotEmpty"`
FamilyMembers IntType `validate:"min=1,max=100"`
SomeFloat FloatType `validate:"min=2.55,max=99.99"`
SomeMap MapType `validate:"min_items=2,key=[max_len=64],value=[min=-35,max=34]"`
SomePointer *StringType `validate:"not_null,min_len=20,max_len=150"`
}
79 changes: 79 additions & 0 deletions examples/aliases/entities_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package aliases

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func Test_User_Validate(t *testing.T) {
t.Parallel()

someString := StringType("aaaaaaaaaaaaaaaaaaaab")
validUser := User{
FirstName: "firstName",
LastName: "lastName",
NonEmptyString: "test",
FamilyMembers: 99,
SomeFloat: 5.55,
SomeMap: map[string]int{
"test": 1,
"test2": 2,
},
SomePointer: &someString,
}

t.Run("valid", func(t *testing.T) {
assert.NoError(t, validUser.Validate())
})

t.Run("invalid", func(t *testing.T) {

t.Run("first_name: too short, using overridden alias validator", func(t *testing.T) {
r := validUser
r.FirstName = "aa"

err := r.Validate()
require.NoError(t, err)
})

t.Run("first_name: too short, using alias validator", func(t *testing.T) {
r := validUser
r.FirstName = "aa"

err := r.FirstName.Validate()
require.NotNil(t, err)
assert.Equal(t, `shorter than 3 chars`, err.Error())
})

t.Run("some_pointer: not_null rule", func(t *testing.T) {
r := validUser
r.SomePointer = nil
r.NonEmptyString = ""

err := r.Validate()
require.NotNil(t, err)
assert.Equal(t, `[NonEmptyString: string is empty, SomePointer: cannot be nil]`, err.Error())
})

t.Run("some_pointer: using overridden alias validator", func(t *testing.T) {
r := validUser
someString := StringType("aaa")
r.SomePointer = &someString

err := r.Validate()
require.NotNil(t, err)
assert.Equal(t, `[SomePointer: shorter than 20 chars]`, err.Error())
})

t.Run("non_empty_string: using func validator", func(t *testing.T) {
r := validUser
r.NonEmptyString = ""

err := r.Validate()
require.NotNil(t, err)
assert.Equal(t, `[NonEmptyString: string is empty]`, err.Error())
})
})
}
99 changes: 99 additions & 0 deletions examples/aliases/validators.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//This file was automatically generated by the genval generator v1.4
//Please don't modify it manually. Edit your entity tags and then
//run go generate

package aliases

import (
"fmt"

"github.com/gojuno/genval/errlist"

"unicode/utf8"
)

type validatable interface {
Validate() error
}

func validate(i interface{}) error {
if v, ok := i.(validatable); ok {
return v.Validate()
}
return nil
}

// Validate validates FloatType
func (r FloatType) Validate() error {
return nil
}

// Validate validates IntType
func (r IntType) Validate() error {
return nil
}

// Validate validates MapType
func (r MapType) Validate() error {
var errs errlist.List
return errs.ErrorOrNil()
}

// Validate validates User
func (r User) Validate() error {
var errs errlist.List
if utf8.RuneCountInString(string(r.FirstName)) < 2 {
errs.AddFieldf("FirstName", "shorter than 2 chars")
}
if utf8.RuneCountInString(string(r.FirstName)) > 15 {
errs.AddFieldf("FirstName", "longer than 15 chars")
}
if utf8.RuneCountInString(string(r.LastName)) < 1 {
errs.AddFieldf("LastName", "shorter than 1 chars")
}
if utf8.RuneCountInString(string(r.LastName)) > 15 {
errs.AddFieldf("LastName", "longer than 15 chars")
}
if err := r.NonEmptyString.ValidateNotEmpty(); err != nil {
errs.AddField("NonEmptyString", err)
}
if r.FamilyMembers < 1 {
errs.AddFieldf("FamilyMembers", "less than 1")
}
if r.FamilyMembers > 100 {
errs.AddFieldf("FamilyMembers", "more than 100")
}
if r.SomeFloat < 2.55 {
errs.AddFieldf("SomeFloat", "less than 2.55")
}
if r.SomeFloat > 99.99 {
errs.AddFieldf("SomeFloat", "more than 99.99")
}
if len(r.SomeMap) < 2 {
errs.AddFieldf("SomeMap", "less items than 2")
}
for SomeMapKey, SomeMapValue := range r.SomeMap {
_ = SomeMapKey
_ = SomeMapValue
if utf8.RuneCountInString(string(SomeMapKey)) > 64 {
errs.AddFieldf(fmt.Sprintf("SomeMap"+".%v", SomeMapKey), "longer than 64 chars")
}
if SomeMapValue < -35 {
errs.AddFieldf(fmt.Sprintf("SomeMap"+".%v", SomeMapKey), "less than -35")
}
if SomeMapValue > 34 {
errs.AddFieldf(fmt.Sprintf("SomeMap"+".%v", SomeMapKey), "more than 34")
}
}
if r.SomePointer == nil {
errs.AddFieldf("SomePointer", "cannot be nil")
} else {
if utf8.RuneCountInString(string(*r.SomePointer)) < 20 {
errs.AddFieldf("SomePointer", "shorter than 20 chars")
}
if utf8.RuneCountInString(string(*r.SomePointer)) > 150 {
errs.AddFieldf("SomePointer", "longer than 150 chars")
}
}
return errs.ErrorOrNil()
}
8 changes: 0 additions & 8 deletions examples/complicated/entities.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,3 @@ func (r AliasOnDogsMapAlias) ValidateAlias() error {
}
return nil
}

type AliasString string

type AliasArray []string

type AliasFunc func() string

type AliasChan <-chan string
Loading

0 comments on commit 8ea5e7f

Please sign in to comment.