func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, isStructField bool, tag, name, customName string, partial bool, exclude bool, includeExclude map[string]*struct{}, cTag *cachedTag) {
	if tag == skipValidationTag {
		return
	}
	if cTag == nil {
		var isCached bool
		cTag, isCached = v.tagCache.Get(tag)
		if !isCached {
			cTag = v.parseTags(tag, name)
		}
	}
	current, kind := v.ExtractType(current)
	var typ reflect.Type
	switch kind {
	case reflect.Ptr, reflect.Interface, reflect.Invalid:
		if cTag.isOmitEmpty {
			return
		}
		if tag != blank {
			ns := errPrefix + name
			if kind == reflect.Invalid {
				errs[ns] = &FieldError{
					FieldNamespace: ns,
					NameNamespace:  nsPrefix + customName,
					Name:           customName,
					Field:          name,
					Tag:            cTag.tags[0].tag,
					ActualTag:      cTag.tags[0].tagVals[0][0],
					Param:          cTag.tags[0].tagVals[0][1],
					Kind:           kind,
				}
				return
			}
			errs[ns] = &FieldError{
				FieldNamespace: ns,
				NameNamespace:  nsPrefix + customName,
				Name:           customName,
				Field:          name,
				Tag:            cTag.tags[0].tag,
				ActualTag:      cTag.tags[0].tagVals[0][0],
				Param:          cTag.tags[0].tagVals[0][1],
				Value:          current.Interface(),
				Kind:           kind,
				Type:           current.Type(),
			}
			return
		}
		if kind == reflect.Invalid {
			return
		}
	case reflect.Struct:
		typ = current.Type()
		if typ != timeType {
			if cTag.isNoStructLevel {
				return
			}
			v.tranverseStruct(topStruct, current, current, errPrefix+name+namespaceSeparator, nsPrefix+customName+namespaceSeparator, errs, false, partial, exclude, includeExclude, cTag.isStructOnly)
			return
		}
	}
	if tag == blank {
		return
	}
	typ = current.Type()
	var dive bool
	var diveSubTag string
	for _, valTag := range cTag.tags {
		if valTag.tagVals[0][0] == existsTag {
			continue
		}
		if valTag.tagVals[0][0] == diveTag {
			dive = true
			diveSubTag = strings.TrimLeft(strings.SplitN(cTag.diveTag, diveTag, 2)[1], ",")
			break
		}
		if valTag.tagVals[0][0] == omitempty {
			if !HasValue(v, topStruct, currentStruct, current, typ, kind, blank) {
				return
			}
			continue
		}
		if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, nsPrefix, errs, valTag, name, customName) {
			return
		}
	}
	if dive {
		switch kind {
		case reflect.Slice, reflect.Array:
			v.traverseSlice(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
		case reflect.Map:
			v.traverseMap(topStruct, currentStruct, current, errPrefix, nsPrefix, errs, diveSubTag, name, customName, partial, exclude, includeExclude, nil)
		default:
			panic("dive error! can't dive on a non slice or map")
		}
	}
}
traverseField validates any field, be it a struct or single field, ensures it's validity and passes it along to be validated via it's tag options
package validator
import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"sync"
	"time"
)
const (
	utf8HexComma            = "0x2C"
	utf8Pipe                = "0x7C"
	tagSeparator            = ","
	orSeparator             = "|"
	tagKeySeparator         = "="
	structOnlyTag           = "structonly"
	noStructLevelTag        = "nostructlevel"
	omitempty               = "omitempty"
	skipValidationTag       = "-"
	diveTag                 = "dive"
	existsTag               = "exists"
	fieldErrMsg             = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
	arrayIndexFieldName     = "%s" + leftBracket + "%d" + rightBracket
	mapIndexFieldName       = "%s" + leftBracket + "%v" + rightBracket
	invalidValidation       = "Invalid validation tag on field %s"
	undefinedValidation     = "Undefined validation function on field %s"
	validatorNotInitialized = "Validator instance not initialized"
	fieldNameRequired       = "Field Name Required"
	tagRequired             = "Tag Required"
)
var (
	timeType       = reflect.TypeOf(time.Time{})
	timePtrType    = reflect.TypeOf(&time.Time{})
	emptyStructPtr = new(struct{})
)
type StructLevel struct {
	TopStruct     reflect.Value
	CurrentStruct reflect.Value
	errPrefix     string
	nsPrefix      string
	errs          ValidationErrors
	v             *Validate
}
func (sl *StructLevel) ReportValidationErrors(relativeKey string, errs ValidationErrors) {
	for _, e := range errs {
		idx := strings.Index(relativeKey, "|")
		var rel string
		var cRel string
		if idx != -1 {
			rel = relativeKey[:idx]
			cRel = relativeKey[idx+1:]
		} else {
			rel = relativeKey
		}
		key := sl.errPrefix + rel + e.Field
		e.FieldNamespace = key
		e.NameNamespace = sl.nsPrefix + cRel + e.Name
		sl.errs[key] = e
	}
}
func (sl *StructLevel) ReportError(field reflect.Value, fieldName string, customName string, tag string) {
	field, kind := sl.v.ExtractType(field)
	if fieldName == blank {
		panic(fieldNameRequired)
	}
	if customName == blank {
		customName = fieldName
	}
	if tag == blank {
		panic(tagRequired)
	}
	ns := sl.errPrefix + fieldName
	switch kind {
	case reflect.Invalid:
		sl.errs[ns] = &FieldError{
			FieldNamespace: ns,
			NameNamespace:  sl.nsPrefix + customName,
			Name:           customName,
			Field:          fieldName,
			Tag:            tag,
			ActualTag:      tag,
			Param:          blank,
			Kind:           kind,
		}
	default:
		sl.errs[ns] = &FieldError{
			FieldNamespace: ns,
			NameNamespace:  sl.nsPrefix + customName,
			Name:           customName,
			Field:          fieldName,
			Tag:            tag,
			ActualTag:      tag,
			Param:          blank,
			Value:          field.Interface(),
			Kind:           kind,
			Type:           field.Type(),
		}
	}
}
type Validate struct {
	tagName             string
	fieldNameTag        string
	validationFuncs     map[string]Func
	structLevelFuncs    map[reflect.Type]StructLevelFunc
	customTypeFuncs     map[reflect.Type]CustomTypeFunc
	aliasValidators     map[string]string
	hasCustomFuncs      bool
	hasAliasValidators  bool
	hasStructLevelFuncs bool
	tagCache            *tagCacheMap
	structCache         *structCacheMap
	errsPool            *sync.Pool
}
func (v *Validate) initCheck() {
	if v == nil {
		panic(validatorNotInitialized)
	}
}
type Config struct {
	TagName      string
	FieldNameTag string
}
type CustomTypeFunc func(field reflect.Value) interface{}
type Func func(v *Validate, topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool
type StructLevelFunc func(v *Validate, structLevel *StructLevel)
type ValidationErrors map[string]*FieldError
func (ve ValidationErrors) Error() string {
	buff := bytes.NewBufferString(blank)
	for key, err := range ve {
		buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag))
		buff.WriteString("\n")
	}
	return strings.TrimSpace(buff.String())
}
type FieldError struct {
	FieldNamespace string
	NameNamespace  string
	Field          string
	Name           string
	Tag            string
	ActualTag      string
	Kind           reflect.Kind
	Type           reflect.Type
	Param          string
	Value          interface{}
}
func New(config *Config) *Validate {
	v := &Validate{
		tagName:      config.TagName,
		fieldNameTag: config.FieldNameTag,
		tagCache:     &tagCacheMap{m: map[string]*cachedTag{}},
		structCache:  &structCacheMap{m: map[reflect.Type]*cachedStruct{}},
		errsPool: &sync.Pool{New: func() interface{} {
			return ValidationErrors{}
		}}}
	if len(v.aliasValidators) == 0 {
		v.aliasValidators = map[string]string{}
		for k, val := range bakedInAliasValidators {
			v.RegisterAliasValidation(k, val)
		}
	}
	if len(v.validationFuncs) == 0 {
		v.validationFuncs = map[string]Func{}
		for k, val := range bakedInValidators {
			v.RegisterValidation(k, val)
		}
	}
	return v
}
func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interface{}) {
	v.initCheck()
	if v.structLevelFuncs == nil {
		v.structLevelFuncs = map[reflect.Type]StructLevelFunc{}
	}
	for _, t := range types {
		v.structLevelFuncs[reflect.TypeOf(t)] = fn
	}
	v.hasStructLevelFuncs = true
}
func (v *Validate) RegisterValidation(key string, fn Func) error {
	v.initCheck()
	if key == blank {
		return errors.New("Function Key cannot be empty")
	}
	if fn == nil {
		return errors.New("Function cannot be empty")
	}
	_, ok := restrictedTags[key]
	if ok || strings.ContainsAny(key, restrictedTagChars) {
		panic(fmt.Sprintf(restrictedTagErr, key))
	}
	v.validationFuncs[key] = fn
	return nil
}
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
	v.initCheck()
	if v.customTypeFuncs == nil {
		v.customTypeFuncs = map[reflect.Type]CustomTypeFunc{}
	}
	for _, t := range types {
		v.customTypeFuncs[reflect.TypeOf(t)] = fn
	}
	v.hasCustomFuncs = true
}
func (v *Validate) RegisterAliasValidation(alias, tags string) {
	v.initCheck()
	_, ok := restrictedTags[alias]
	if ok || strings.ContainsAny(alias, restrictedTagChars) {
		panic(fmt.Sprintf(restrictedAliasErr, alias))
	}
	v.aliasValidators[alias] = tags
	v.hasAliasValidators = true
}
func (v *Validate) Field(field interface{}, tag string) error {
	v.initCheck()
	errs := v.errsPool.Get().(ValidationErrors)
	fieldVal := reflect.ValueOf(field)
	v.traverseField(fieldVal, fieldVal, fieldVal, blank, blank, errs, false, tag, blank, blank, false, false, nil, nil)
	if len(errs) == 0 {
		v.errsPool.Put(errs)
		return nil
	}
	return errs
}
func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) error {
	v.initCheck()
	errs := v.errsPool.Get().(ValidationErrors)
	topVal := reflect.ValueOf(val)
	v.traverseField(topVal, topVal, reflect.ValueOf(field), blank, blank, errs, false, tag, blank, blank, false, false, nil, nil)
	if len(errs) == 0 {
		v.errsPool.Put(errs)
		return nil
	}
	return errs
}
func (v *Validate) StructPartial(current interface{}, fields ...string) error {
	v.initCheck()
	sv, _ := v.ExtractType(reflect.ValueOf(current))
	name := sv.Type().Name()
	m := map[string]*struct{}{}
	if fields != nil {
		for _, k := range fields {
			flds := strings.Split(k, namespaceSeparator)
			if len(flds) > 0 {
				key := name + namespaceSeparator
				for _, s := range flds {
					idx := strings.Index(s, leftBracket)
					if idx != -1 {
						for idx != -1 {
							key += s[:idx]
							m[key] = emptyStructPtr
							idx2 := strings.Index(s, rightBracket)
							idx2++
							key += s[idx:idx2]
							m[key] = emptyStructPtr
							s = s[idx2:]
							idx = strings.Index(s, leftBracket)
						}
					} else {
						key += s
						m[key] = emptyStructPtr
					}
					key += namespaceSeparator
				}
			}
		}
	}
	errs := v.errsPool.Get().(ValidationErrors)
	v.tranverseStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, false, m, false)
	if len(errs) == 0 {
		v.errsPool.Put(errs)
		return nil
	}
	return errs
}
func (v *Validate) StructExcept(current interface{}, fields ...string) error {
	v.initCheck()
	sv, _ := v.ExtractType(reflect.ValueOf(current))
	name := sv.Type().Name()
	m := map[string]*struct{}{}
	for _, key := range fields {
		m[name+namespaceSeparator+key] = emptyStructPtr
	}
	errs := v.errsPool.Get().(ValidationErrors)
	v.tranverseStruct(sv, sv, sv, blank, blank, errs, true, len(m) != 0, true, m, false)
	if len(errs) == 0 {
		v.errsPool.Put(errs)
		return nil
	}
	return errs
}
func (v *Validate) Struct(current interface{}) error {
	v.initCheck()
	errs := v.errsPool.Get().(ValidationErrors)
	sv := reflect.ValueOf(current)
	v.tranverseStruct(sv, sv, sv, blank, blank, errs, true, false, false, nil, false)
	if len(errs) == 0 {
		v.errsPool.Put(errs)
		return nil
	}
	return errs
}
func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, nsPrefix string, errs ValidationErrors, useStructName bool, partial bool, exclude bool, includeExclude map[string]*struct{}, isStructOnly bool) {
	if current.Kind() == reflect.Ptr && !current.IsNil() {
		current = current.Elem()
	}
	if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface {
		panic("value passed for validation is not a struct")
	}
	typ := current.Type()
	sName := typ.Name()
	if useStructName {
		errPrefix += sName + namespaceSeparator
		if v.fieldNameTag != blank {
			nsPrefix += sName + namespaceSeparator
		}
	}
	if !isStructOnly {
		var fld reflect.StructField
		if sName == blank {
			var customName string
			var ok bool
			numFields := current.NumField()
			for i := 0; i < numFields; i++ {
				fld = typ.Field(i)
				if fld.PkgPath != blank && !fld.Anonymous {
					continue
				}
				if partial {
					_, ok = includeExclude[errPrefix+fld.Name]
					if (ok && exclude) || (!ok && !exclude) {
						continue
					}
				}
				customName = fld.Name
				if v.fieldNameTag != blank {
					name := strings.SplitN(fld.Tag.Get(v.fieldNameTag), ",", 2)[0]
					if name != blank && name != dash {
						customName = name
					}
				}
				v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, nsPrefix, errs, true, fld.Tag.Get(v.tagName), fld.Name, customName, partial, exclude, includeExclude, nil)
			}
		} else {
			s, ok := v.structCache.Get(typ)
			if !ok {
				s = v.parseStruct(current, sName)
			}
			for i, f := range s.fields {
				if partial {
					_, ok = includeExclude[errPrefix+f.Name]
					if (ok && exclude) || (!ok && !exclude) {
						continue
					}
				}
				fld = typ.Field(i)
				v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, nsPrefix, errs, true, f.CachedTag.tag, fld.Name, f.AltName, partial, exclude, includeExclude, f.CachedTag)
			}
		}
	}
	if v.hasStructLevelFuncs {
		if fn, ok := v.structLevelFuncs[current.Type()]; ok {
			fn(v, &StructLevel{v: v, TopStruct: topStruct, CurrentStruct: current, errPrefix: errPrefix, nsPrefix: nsPrefix, errs: errs})
		}
	}
}