With helper functions we can make common types of validation much easier. I
propose two categories of helper functions:
- Value Assertion: Helpers which assert a specific state/property of a given
value, returning a boolean to indicate if the given value meets the condition
or not. These functions should typically have a name which starts with Is
,
Has
, or similar words. For example: IsNil()
, HasLen()
- Validation: Helpers which will perform a specific type of check against a
value, and return nil on success, or a *Error
on failure. Under the hood
they'll use Value Assertion functions, along with custom logic as needed.
These functions should typically have a name which starts with Require
or
Allow
. For example: RequireField()
, RequireLen()
, AllowFieldWhen()
Value Assertion helpers
Value assertion helpers simply return a boolean value indicating if the given
value meets the condition of the helper or not. These can be used directly in
custom Validate()
functions, and they are also used within validation helper
functions too.
Functions
Below is rough list of value assertion helpers I think are useful, please
suggest changes to this list as you see fit.
Validation helpers
Validation helpers verify if given values meets specific conditions, returning
nil
on success, and a *Error
if it fails that describes why the validation
failed.
There are two variants of all validation helpers, the "direct validation" and
"field validation". The direct variants validates the given value directly, and
does not set a Field
value on the returned *Error
object. While a field
validation helper accepts an instance of a struct, and one or more fields as
strings, and performs the validation against the given field(s) on the given
struct, and sets the Field
value as needed on the returned *Error
.
Translations / i18n
While I believe providing multi-language translations for built-in validation
helpers is probably outside the scope of this project right now, we should
strive to make translations possible and hopefully easy to implement if/when
needed.
As such, I propose we create *Validation
structs for each type of validation,
which contains all the input fields needed to produce a human readable error
message, along with a Error() string
function that produces the English
language string for the error.
For example, using the RequireLenBetween(v interface{}, min, max int) error
helper described below, we could have a struct like this:
type RequireLenBetweenValidation struct {
Min int
Max int
}
func (s *RequireLenBetweenValidation) Error() string {
return fmt.Sprintf(
"must have a length equal to or greater than %d " +
"and equal to or less than %d",
s.Min, s.Max,
)
}
This enables Validate()
functions to return *RequireLenBetweenValidation
instances directly which will be wrapped up in a *Error
with the field set
correctly.
RequireFieldLenBetween(s interface{}, field string, min, max int) error
helper
described below, would simply wrap the validation error struct in a *Error
directly with the field set accordingly.
This should allow easy translation by performing type assertions on the Err
value for each type of validation error you want to support translating.
Though effectively it means validation translations happen on he returned errors
after executing validation, rather than it being built-in to the validation
package itself. It simply exposes enough information about each validation error
to implement your own humanly readable error in any language you see fit.
Functions
Below is rough list of validation helpers I think are useful, please suggest
changes to this list as you see fit.
Require
Validates value is not nil or empty as determined by IsBlank()
.
Error message on failure:
RequireNotNil
Validates value is not nil as determined by IsNil()
.
Error message on failure:
RequireNotEmpty
Validates value is not empty as determined by IsEmpty()
.
Error message on failure:
RequireLen
Validates value has a length of exactly n
.
Error message on failure:
"must have a length of exactly <n>"
RequireMinLen
Validates value has a length equal to or greater than min
.
Error message on failure:
"must have a length equal to or greater than <min>"
RequireMaxLen
Validates value has a length equal to or less than max
.
Error message on failure:
"must have a length equal to or greater than <max>"
RequireLenBetween
Validates that the given value has a length equal to or greater than min
and
equal to or less than max
.
"must have a length equal to or greater than <min> and equal to or less than <max>"
RequireOneOf
Validates that only one of specified fields
on struct s
has a non-nil or
non-empty value as determined by IsBlank()
.
Error message on failure:
"exactly one of 'Field1', 'Field2', or 'Field3' must be specified"
RequireExactlyNOf
Validates that exactly n
of specified fields
on struct s
has a non-nil or
non-empty value as determined by IsBlank()
.
Error message on failure:
"exactly <n> of 'Field1', 'Field2', or 'Field3' must be specified"
RequireFieldWhen
When field other
on struct s
is equal to value in v
, validates that field
field
on struct s
is not nil or empty as determined by IsBlank()
.
Error message when other
is "Type"
and v
is "Book"
:
"is required when Type='Book'"
AllowFieldWhen
When field other
on struct s
is not equal to value in v
, validates that
field field
on struct s
is nil or empty as determined by IsBlank()
.
Effectively prohibiting setting field field
if field other
is not equal to
v
.
Error message when other
is "Type"
and v
is "Book"
:
"is only allowed when Type='Book'"