errors.go raw

   1  package validator
   2  
   3  import (
   4  	"bytes"
   5  	"fmt"
   6  	"reflect"
   7  	"strings"
   8  
   9  	ut "github.com/go-playground/universal-translator"
  10  )
  11  
  12  const (
  13  	fieldErrMsg = "Key: '%s' Error:Field validation for '%s' failed on the '%s' tag"
  14  )
  15  
  16  // ValidationErrorsTranslations is the translation return type
  17  type ValidationErrorsTranslations map[string]string
  18  
  19  // InvalidValidationError describes an invalid argument passed to
  20  // `Struct`, `StructExcept`, StructPartial` or `Field`
  21  type InvalidValidationError struct {
  22  	Type reflect.Type
  23  }
  24  
  25  // Error returns InvalidValidationError message
  26  func (e *InvalidValidationError) Error() string {
  27  
  28  	if e.Type == nil {
  29  		return "validator: (nil)"
  30  	}
  31  
  32  	return "validator: (nil " + e.Type.String() + ")"
  33  }
  34  
  35  // ValidationErrors is an array of FieldError's
  36  // for use in custom error messages post validation.
  37  type ValidationErrors []FieldError
  38  
  39  // Error is intended for use in development + debugging and not intended to be a production error message.
  40  // It allows ValidationErrors to subscribe to the Error interface.
  41  // All information to create an error message specific to your application is contained within
  42  // the FieldError found within the ValidationErrors array
  43  func (ve ValidationErrors) Error() string {
  44  
  45  	buff := bytes.NewBufferString("")
  46  
  47  	for i := 0; i < len(ve); i++ {
  48  
  49  		buff.WriteString(ve[i].Error())
  50  		buff.WriteString("\n")
  51  	}
  52  
  53  	return strings.TrimSpace(buff.String())
  54  }
  55  
  56  // Translate translates all of the ValidationErrors
  57  func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
  58  
  59  	trans := make(ValidationErrorsTranslations)
  60  
  61  	var fe *fieldError
  62  
  63  	for i := 0; i < len(ve); i++ {
  64  		fe = ve[i].(*fieldError)
  65  
  66  		// // in case an Anonymous struct was used, ensure that the key
  67  		// // would be 'Username' instead of ".Username"
  68  		// if len(fe.ns) > 0 && fe.ns[:1] == "." {
  69  		// 	trans[fe.ns[1:]] = fe.Translate(ut)
  70  		// 	continue
  71  		// }
  72  
  73  		trans[fe.ns] = fe.Translate(ut)
  74  	}
  75  
  76  	return trans
  77  }
  78  
  79  // FieldError contains all functions to get error details
  80  type FieldError interface {
  81  
  82  	// Tag returns the validation tag that failed. if the
  83  	// validation was an alias, this will return the
  84  	// alias name and not the underlying tag that failed.
  85  	//
  86  	// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
  87  	// will return "iscolor"
  88  	Tag() string
  89  
  90  	// ActualTag returns the validation tag that failed, even if an
  91  	// alias the actual tag within the alias will be returned.
  92  	// If an 'or' validation fails the entire or will be returned.
  93  	//
  94  	// eg. alias "iscolor": "hexcolor|rgb|rgba|hsl|hsla"
  95  	// will return "hexcolor|rgb|rgba|hsl|hsla"
  96  	ActualTag() string
  97  
  98  	// Namespace returns the namespace for the field error, with the tag
  99  	// name taking precedence over the field's actual name.
 100  	//
 101  	// eg. JSON name "User.fname"
 102  	//
 103  	// See StructNamespace() for a version that returns actual names.
 104  	//
 105  	// NOTE: this field can be blank when validating a single primitive field
 106  	// using validate.Field(...) as there is no way to extract it's name
 107  	Namespace() string
 108  
 109  	// StructNamespace returns the namespace for the field error, with the field's
 110  	// actual name.
 111  	//
 112  	// eq. "User.FirstName" see Namespace for comparison
 113  	//
 114  	// NOTE: this field can be blank when validating a single primitive field
 115  	// using validate.Field(...) as there is no way to extract its name
 116  	StructNamespace() string
 117  
 118  	// Field returns the fields name with the tag name taking precedence over the
 119  	// field's actual name.
 120  	//
 121  	// eq. JSON name "fname"
 122  	// see StructField for comparison
 123  	Field() string
 124  
 125  	// StructField returns the field's actual name from the struct, when able to determine.
 126  	//
 127  	// eq.  "FirstName"
 128  	// see Field for comparison
 129  	StructField() string
 130  
 131  	// Value returns the actual field's value in case needed for creating the error
 132  	// message
 133  	Value() interface{}
 134  
 135  	// Param returns the param value, in string form for comparison; this will also
 136  	// help with generating an error message
 137  	Param() string
 138  
 139  	// Kind returns the Field's reflect Kind
 140  	//
 141  	// eg. time.Time's kind is a struct
 142  	Kind() reflect.Kind
 143  
 144  	// Type returns the Field's reflect Type
 145  	//
 146  	// eg. time.Time's type is time.Time
 147  	Type() reflect.Type
 148  
 149  	// Translate returns the FieldError's translated error
 150  	// from the provided 'ut.Translator' and registered 'TranslationFunc'
 151  	//
 152  	// NOTE: if no registered translator can be found it returns the same as
 153  	// calling fe.Error()
 154  	Translate(ut ut.Translator) string
 155  
 156  	// Error returns the FieldError's message
 157  	Error() string
 158  }
 159  
 160  // compile time interface checks
 161  var _ FieldError = new(fieldError)
 162  var _ error = new(fieldError)
 163  
 164  // fieldError contains a single field's validation error along
 165  // with other properties that may be needed for error message creation
 166  // it complies with the FieldError interface
 167  type fieldError struct {
 168  	v              *Validate
 169  	tag            string
 170  	actualTag      string
 171  	ns             string
 172  	structNs       string
 173  	fieldLen       uint8
 174  	structfieldLen uint8
 175  	value          interface{}
 176  	param          string
 177  	kind           reflect.Kind
 178  	typ            reflect.Type
 179  }
 180  
 181  // Tag returns the validation tag that failed.
 182  func (fe *fieldError) Tag() string {
 183  	return fe.tag
 184  }
 185  
 186  // ActualTag returns the validation tag that failed, even if an
 187  // alias the actual tag within the alias will be returned.
 188  func (fe *fieldError) ActualTag() string {
 189  	return fe.actualTag
 190  }
 191  
 192  // Namespace returns the namespace for the field error, with the tag
 193  // name taking precedence over the field's actual name.
 194  func (fe *fieldError) Namespace() string {
 195  	return fe.ns
 196  }
 197  
 198  // StructNamespace returns the namespace for the field error, with the field's
 199  // actual name.
 200  func (fe *fieldError) StructNamespace() string {
 201  	return fe.structNs
 202  }
 203  
 204  // Field returns the field's name with the tag name taking precedence over the
 205  // field's actual name.
 206  func (fe *fieldError) Field() string {
 207  
 208  	return fe.ns[len(fe.ns)-int(fe.fieldLen):]
 209  	// // return fe.field
 210  	// fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]
 211  
 212  	// log.Println("FLD:", fld)
 213  
 214  	// if len(fld) > 0 && fld[:1] == "." {
 215  	// 	return fld[1:]
 216  	// }
 217  
 218  	// return fld
 219  }
 220  
 221  // StructField returns the field's actual name from the struct, when able to determine.
 222  func (fe *fieldError) StructField() string {
 223  	// return fe.structField
 224  	return fe.structNs[len(fe.structNs)-int(fe.structfieldLen):]
 225  }
 226  
 227  // Value returns the actual field's value in case needed for creating the error
 228  // message
 229  func (fe *fieldError) Value() interface{} {
 230  	return fe.value
 231  }
 232  
 233  // Param returns the param value, in string form for comparison; this will
 234  // also help with generating an error message
 235  func (fe *fieldError) Param() string {
 236  	return fe.param
 237  }
 238  
 239  // Kind returns the Field's reflect Kind
 240  func (fe *fieldError) Kind() reflect.Kind {
 241  	return fe.kind
 242  }
 243  
 244  // Type returns the Field's reflect Type
 245  func (fe *fieldError) Type() reflect.Type {
 246  	return fe.typ
 247  }
 248  
 249  // Error returns the fieldError's error message
 250  func (fe *fieldError) Error() string {
 251  	return fmt.Sprintf(fieldErrMsg, fe.ns, fe.Field(), fe.tag)
 252  }
 253  
 254  // Translate returns the FieldError's translated error
 255  // from the provided 'ut.Translator' and registered 'TranslationFunc'
 256  //
 257  // NOTE: if no registered translation can be found, it returns the original
 258  // untranslated error message.
 259  func (fe *fieldError) Translate(ut ut.Translator) string {
 260  	var fn TranslationFunc
 261  
 262  	m, ok := fe.v.transTagFunc[ut]
 263  	if !ok {
 264  		return fe.Error()
 265  	}
 266  
 267  	fn, ok = m[fe.tag]
 268  	if !ok {
 269  		fn, ok = m[fe.actualTag]
 270  		if !ok {
 271  			return fe.Error()
 272  		}
 273  	}
 274  
 275  	return fn(ut, fe)
 276  }
 277