Godeps/_workspace/src/gopkg.in/bluesuncorp/validator.v5/validator.go
func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors {

	structValue := reflect.ValueOf(s)

	if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
		return v.structRecursive(top, current, structValue.Elem().Interface())
	}

	if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
		panic("interface passed for validation is not a struct")
	}

	structType := reflect.TypeOf(s)

	var structName string
	var numFields int
	var cs *cachedStruct
	var isCached bool

	cs, isCached = structCache.Get(structType)

	if isCached {
		structName = cs.name
		numFields = cs.children
	} else {
		structName = structType.Name()
		numFields = structValue.NumField()
		cs = &cachedStruct{name: structName, children: numFields}
	}

	validationErrors := structPool.Get().(*StructErrors)
	validationErrors.Struct = structName

	for i := 0; i < numFields; i++ {

		var valueField reflect.Value
		var cField *cachedField
		var typeField reflect.StructField

		if isCached {
			cField = cs.fields[i]
			valueField = structValue.Field(cField.index)

			if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
				valueField = valueField.Elem()
			}
		} else {
			valueField = structValue.Field(i)

			if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
				valueField = valueField.Elem()
			}

			typeField = structType.Field(i)

			cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))}

			if cField.tag == noValidationTag {
				cs.children--
				continue
			}

			// if no validation and not a struct (which may containt fields for validation)
			if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) {
				cs.children--
				continue
			}

			cField.name = typeField.Name
			cField.kind = valueField.Kind()
			cField.typ = valueField.Type()
		}

		// this can happen if the first cache value was nil
		// but the second actually has a value
		if cField.kind == reflect.Ptr {
			cField.kind = valueField.Kind()
		}

		switch cField.kind {

		case reflect.Struct, reflect.Interface:

			if !unicode.IsUpper(rune(cField.name[0])) {
				cs.children--
				continue
			}

			if cField.isTime {

				if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
					validationErrors.Errors[fieldError.Field] = fieldError
					// free up memory reference
					fieldError = nil
				}

			} else {

				if strings.Contains(cField.tag, structOnlyTag) {
					cs.children--
					continue
				}

				if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {

					if strings.Contains(cField.tag, omitempty) {
						goto CACHEFIELD
					}

					tags := strings.Split(cField.tag, tagSeparator)

					if len(tags) > 0 {

						var param string
						vals := strings.SplitN(tags[0], tagKeySeparator, 2)

						if len(vals) > 1 {
							param = vals[1]
						}

						validationErrors.Errors[cField.name] = &FieldError{
							Field: cField.name,
							Tag:   vals[0],
							Param: param,
							Value: valueField.Interface(),
							Kind:  valueField.Kind(),
							Type:  valueField.Type(),
						}

						goto CACHEFIELD
					}
				}

				// if we get here, the field is interface and could be a struct or a field
				// and we need to check the inner type and validate
				if cField.kind == reflect.Interface {

					valueField = valueField.Elem()

					if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
						valueField = valueField.Elem()
					}

					if valueField.Kind() == reflect.Struct {
						goto VALIDATESTRUCT
					}

					// sending nil for cField as it was type interface and could be anything
					// each time and so must be calculated each time and can't be cached reliably
					if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
						validationErrors.Errors[fieldError.Field] = fieldError
						// free up memory reference
						fieldError = nil
					}

					goto CACHEFIELD
				}

			VALIDATESTRUCT:
				if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
					validationErrors.StructErrors[cField.name] = structErrors
					// free up memory map no longer needed
					structErrors = nil
				}
			}

		case reflect.Slice, reflect.Array:
			cField.isSliceOrArray = true
			cField.sliceSubtype = cField.typ.Elem()
			cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{}))
			cField.sliceSubKind = cField.sliceSubtype.Kind()

			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}

		case reflect.Map:
			cField.isMap = true
			cField.mapSubtype = cField.typ.Elem()
			cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{}))
			cField.mapSubKind = cField.mapSubtype.Kind()

			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}

		default:
			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}
		}

	CACHEFIELD:
		if !isCached {
			cs.fields = append(cs.fields, cField)
		}
	}

	structCache.Set(structType, cs)

	if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
		structPool.Put(validationErrors)
		return nil
	}

	return validationErrors
}
func (v *Validate) structRecursive(top interface{}, current interface{}, s interface{}) *StructErrors {

	structValue := reflect.ValueOf(s)

	if structValue.Kind() == reflect.Ptr && !structValue.IsNil() {
		return v.structRecursive(top, current, structValue.Elem().Interface())
	}

	if structValue.Kind() != reflect.Struct && structValue.Kind() != reflect.Interface {
		panic("interface passed for validation is not a struct")
	}

	structType := reflect.TypeOf(s)

	var structName string
	var numFields int
	var cs *cachedStruct
	var isCached bool

	cs, isCached = structCache.Get(structType)

	if isCached {
		structName = cs.name
		numFields = cs.children
	} else {
		structName = structType.Name()
		numFields = structValue.NumField()
		cs = &cachedStruct{name: structName, children: numFields}
	}

	validationErrors := structPool.Get().(*StructErrors)
	validationErrors.Struct = structName

	for i := 0; i < numFields; i++ {

		var valueField reflect.Value
		var cField *cachedField
		var typeField reflect.StructField

		if isCached {
			cField = cs.fields[i]
			valueField = structValue.Field(cField.index)

			if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
				valueField = valueField.Elem()
			}
		} else {
			valueField = structValue.Field(i)

			if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
				valueField = valueField.Elem()
			}

			typeField = structType.Field(i)

			cField = &cachedField{index: i, tag: typeField.Tag.Get(v.tagName), isTime: (valueField.Type() == reflect.TypeOf(time.Time{}) || valueField.Type() == reflect.TypeOf(&time.Time{}))}

			if cField.tag == noValidationTag {
				cs.children--
				continue
			}

			// if no validation and not a struct (which may containt fields for validation)
			if cField.tag == "" && ((valueField.Kind() != reflect.Struct && valueField.Kind() != reflect.Interface) || valueField.Type() == reflect.TypeOf(time.Time{})) {
				cs.children--
				continue
			}

			cField.name = typeField.Name
			cField.kind = valueField.Kind()
			cField.typ = valueField.Type()
		}

		// this can happen if the first cache value was nil
		// but the second actually has a value
		if cField.kind == reflect.Ptr {
			cField.kind = valueField.Kind()
		}

		switch cField.kind {

		case reflect.Struct, reflect.Interface:

			if !unicode.IsUpper(rune(cField.name[0])) {
				cs.children--
				continue
			}

			if cField.isTime {

				if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
					validationErrors.Errors[fieldError.Field] = fieldError
					// free up memory reference
					fieldError = nil
				}

			} else {

				if strings.Contains(cField.tag, structOnlyTag) {
					cs.children--
					continue
				}

				if (valueField.Kind() == reflect.Ptr || cField.kind == reflect.Interface) && valueField.IsNil() {

					if strings.Contains(cField.tag, omitempty) {
						goto CACHEFIELD
					}

					tags := strings.Split(cField.tag, tagSeparator)

					if len(tags) > 0 {

						var param string
						vals := strings.SplitN(tags[0], tagKeySeparator, 2)

						if len(vals) > 1 {
							param = vals[1]
						}

						validationErrors.Errors[cField.name] = &FieldError{
							Field: cField.name,
							Tag:   vals[0],
							Param: param,
							Value: valueField.Interface(),
							Kind:  valueField.Kind(),
							Type:  valueField.Type(),
						}

						goto CACHEFIELD
					}
				}

				// if we get here, the field is interface and could be a struct or a field
				// and we need to check the inner type and validate
				if cField.kind == reflect.Interface {

					valueField = valueField.Elem()

					if valueField.Kind() == reflect.Ptr && !valueField.IsNil() {
						valueField = valueField.Elem()
					}

					if valueField.Kind() == reflect.Struct {
						goto VALIDATESTRUCT
					}

					// sending nil for cField as it was type interface and could be anything
					// each time and so must be calculated each time and can't be cached reliably
					if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, nil); fieldError != nil {
						validationErrors.Errors[fieldError.Field] = fieldError
						// free up memory reference
						fieldError = nil
					}

					goto CACHEFIELD
				}

			VALIDATESTRUCT:
				if structErrors := v.structRecursive(top, valueField.Interface(), valueField.Interface()); structErrors != nil {
					validationErrors.StructErrors[cField.name] = structErrors
					// free up memory map no longer needed
					structErrors = nil
				}
			}

		case reflect.Slice, reflect.Array:
			cField.isSliceOrArray = true
			cField.sliceSubtype = cField.typ.Elem()
			cField.isTimeSubtype = (cField.sliceSubtype == reflect.TypeOf(time.Time{}) || cField.sliceSubtype == reflect.TypeOf(&time.Time{}))
			cField.sliceSubKind = cField.sliceSubtype.Kind()

			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}

		case reflect.Map:
			cField.isMap = true
			cField.mapSubtype = cField.typ.Elem()
			cField.isTimeSubtype = (cField.mapSubtype == reflect.TypeOf(time.Time{}) || cField.mapSubtype == reflect.TypeOf(&time.Time{}))
			cField.mapSubKind = cField.mapSubtype.Kind()

			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}

		default:
			if fieldError := v.fieldWithNameAndValue(top, current, valueField.Interface(), cField.tag, cField.name, false, cField); fieldError != nil {
				validationErrors.Errors[fieldError.Field] = fieldError
				// free up memory reference
				fieldError = nil
			}
		}

	CACHEFIELD:
		if !isCached {
			cs.fields = append(cs.fields, cField)
		}
	}

	structCache.Set(structType, cs)

	if len(validationErrors.Errors) == 0 && len(validationErrors.StructErrors) == 0 {
		structPool.Put(validationErrors)
		return nil
	}

	return validationErrors
}
// structRecursive validates a struct recursivly and passes the top level and current struct around for use in validator functions and returns a struct containing the errors