validate.go 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. // Copyright 2014 Manu Martinez-Almeida. All rights reserved.
  2. // Use of this source code is governed by a MIT style
  3. // license that can be found in the LICENSE file.
  4. package binding
  5. import (
  6. "errors"
  7. "reflect"
  8. "strings"
  9. )
  10. func Validate(obj interface{}) error {
  11. return validate(obj, "{{ROOT}}")
  12. }
  13. func validate(obj interface{}, parent string) error {
  14. typ, val := inspectObject(obj)
  15. switch typ.Kind() {
  16. case reflect.Struct:
  17. return validateStruct(typ, val, parent)
  18. case reflect.Slice:
  19. return validateSlice(typ, val, parent)
  20. default:
  21. return errors.New("The object is not a slice or struct.")
  22. }
  23. }
  24. func inspectObject(obj interface{}) (typ reflect.Type, val reflect.Value) {
  25. typ = reflect.TypeOf(obj)
  26. val = reflect.ValueOf(obj)
  27. if typ.Kind() == reflect.Ptr {
  28. typ = typ.Elem()
  29. val = val.Elem()
  30. }
  31. return
  32. }
  33. func validateSlice(typ reflect.Type, val reflect.Value, parent string) error {
  34. if typ.Elem().Kind() == reflect.Struct {
  35. for i := 0; i < val.Len(); i++ {
  36. itemValue := val.Index(i).Interface()
  37. if err := validate(itemValue, parent); err != nil {
  38. return err
  39. }
  40. }
  41. }
  42. return nil
  43. }
  44. func validateStruct(typ reflect.Type, val reflect.Value, parent string) error {
  45. for i := 0; i < typ.NumField(); i++ {
  46. field := typ.Field(i)
  47. // Allow ignored and unexported fields in the struct
  48. // TODO should include || field.Tag.Get("form") == "-"
  49. if len(field.PkgPath) > 0 {
  50. continue
  51. }
  52. fieldValue := val.Field(i).Interface()
  53. requiredField := strings.Index(field.Tag.Get("binding"), "required") > -1
  54. if requiredField {
  55. zero := reflect.Zero(field.Type).Interface()
  56. if reflect.DeepEqual(zero, fieldValue) {
  57. return errors.New("Required " + field.Name + " in " + parent)
  58. }
  59. }
  60. fieldType := field.Type.Kind()
  61. if fieldType == reflect.Struct || fieldType == reflect.Slice {
  62. if err := validate(fieldValue, field.Name); err != nil {
  63. return err
  64. }
  65. }
  66. }
  67. return nil
  68. }