docs: explain what distinguishes validate (+ comment audit) #11
@@ -1,7 +1,85 @@
|
|||||||
# Go Validate
|
# Go Validate
|
||||||
|
|
||||||
A suite of straightforward validation functions. You put something in, you get back `nil` or an error.
|
Small, composable value validators for Go. A validator is just a
|
||||||
|
`func(T) error` — it returns `nil` or an error. You compose them with
|
||||||
|
`All` or `Collect` and drop in plain closures wherever you need something
|
||||||
|
custom. No struct tags, no reflection, no dependencies outside the
|
||||||
|
standard library.
|
||||||
|
|
||||||
|
```go
|
||||||
|
username := validate.All(
|
||||||
|
validate.MinLength(3),
|
||||||
|
validate.MaxLength(16),
|
||||||
|
validate.Chars("abcdefghijklmnopqrstuvwxyz0123456789_"),
|
||||||
|
func(s string) error {
|
||||||
|
if strings.HasPrefix(s, "_") {
|
||||||
|
return errors.New("must not start with an underscore")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := username(input); err != nil {
|
||||||
|
// errors.Is(err, validate.ErrDisallowedChars) still works
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why this and not one of the established packages
|
||||||
|
|
||||||
|
| Package | Style |
|
||||||
|
|---|---|
|
||||||
|
| [`go-playground/validator`](https://github.com/go-playground/validator) | struct tags (`validate:"required,email"`) driven by reflection |
|
||||||
|
| [`go-ozzo/ozzo-validation`](https://github.com/go-ozzo/ozzo-validation) | rules composed in code, through a `Rule` interface over `interface{}` |
|
||||||
|
| [`asaskevich/govalidator`](https://github.com/asaskevich/govalidator) | a bag of `IsEmail` / `IsURL` string helpers |
|
||||||
|
|
||||||
|
This package is the code-composition style (closest to ozzo-validation)
|
||||||
|
with two constraints held on purpose:
|
||||||
|
|
||||||
|
- **Validators are ordinary generic functions.** `In`, `Equal` and the
|
||||||
|
rest are type-checked by the compiler — pass the wrong type and it does
|
||||||
|
not build. The tag/reflection libraries can't do this; they predate
|
||||||
|
generics.
|
||||||
|
- **Errors are plain sentinels.** `errors.Is(err, ErrInvalidEmail)`
|
||||||
|
composes with the standard `errors` package. There is no bespoke
|
||||||
|
`ValidationErrors` type to learn. `Err` matches any error from the
|
||||||
|
package.
|
||||||
|
|
||||||
|
Everything is value-level: there is no struct walker. You wire fields
|
||||||
|
together yourself (a few lines) and decide how to present the result.
|
||||||
|
|
||||||
|
## Fail fast or collect everything
|
||||||
|
|
||||||
|
```go
|
||||||
|
// stops at the first failure
|
||||||
|
validate.All(rules...)
|
||||||
|
|
||||||
|
// runs every rule, joins the failures with errors.Join;
|
||||||
|
// errors.Is still matches each one
|
||||||
|
validate.Collect(rules...)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Building blocks
|
||||||
|
|
||||||
|
| Group | Functions |
|
||||||
|
|---|---|
|
||||||
|
| Compose | `All`, `Collect` |
|
||||||
|
| Presence / equality | `Required`, `Equal`, `In`, `NotIn` |
|
||||||
|
| String length & content | `MinLength`, `MaxLength` (runes), `MinLengthBytes`, `MaxLengthBytes`, `Chars`, `ExceptChars`, `Prefix`, `Suffix`, `Contains`, `Match` |
|
||||||
|
| Formats | `Email`, `URL`, `UUID` |
|
||||||
|
| Numbers | `Min`, `Max`, `MinFloat32`, `MaxFloat32`, `MinFloat64`, `MaxFloat64` |
|
||||||
|
| Slices | `MinSize`, `MaxSize` |
|
||||||
|
| Errors | `Error`, `NewError`, `Err` |
|
||||||
|
|
||||||
|
Each returns (or is) a `func(T) error`, so anything you write with the
|
||||||
|
same shape composes with them.
|
||||||
|
|
||||||
|
## Not a fit if…
|
||||||
|
|
||||||
|
You want **struct-tag validation**, **translated / i18n messages**, or a
|
||||||
|
**large catalogue** of built-in checks (credit cards, ISO codes, CIDRs, …).
|
||||||
|
Use [`go-playground/validator`](https://github.com/go-playground/validator)
|
||||||
|
for that. This package deliberately stays small.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
See [LICENSE.md](./LICENSE.md)
|
MIT. See [LICENSE.md](./LICENSE.md).
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
package validate
|
package validate
|
||||||
|
|
||||||
// All validates a value using a sequence of validation functions.
|
// All validates a value against a sequence of validation functions,
|
||||||
// If any validation function returns an error, the sequence stops and the error is returned.
|
// stopping and returning the first error. See Collect to run every
|
||||||
|
// function and report all failures at once.
|
||||||
func All[T any](fs ...func(T) error) func(T) error {
|
func All[T any](fs ...func(T) error) func(T) error {
|
||||||
return func(value T) error {
|
return func(value T) error {
|
||||||
for _, f := range fs {
|
for _, f := range fs {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ func Chars(allow string) func(string) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExceptChars validates whether a string does not contain disallowed characters.
|
// ExceptChars validates that a string contains none of the given characters.
|
||||||
func ExceptChars(disallow string) func(string) error {
|
func ExceptChars(disallow string) func(string) error {
|
||||||
return func(value string) error {
|
return func(value string) error {
|
||||||
for _, r := range disallow {
|
for _, r := range disallow {
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ func ExampleChars() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ExampleExceptChars() {
|
func ExampleExceptChars() {
|
||||||
testExceptChars := Chars("0123456789abcdef")
|
testExceptChars := ExceptChars("0123456789abcdef")
|
||||||
fmt.Println(testExceptChars("invalid input"))
|
fmt.Println(testExceptChars("invalid input"))
|
||||||
// Output: contains disallowed characters
|
// Output: contains disallowed characters
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
// Package validate provides small, composable value validators.
|
||||||
|
//
|
||||||
|
// A validator is just a func(T) error: it returns nil when the value is
|
||||||
|
// acceptable, or an error describing the problem. Nothing has to implement
|
||||||
|
// an interface, so a plain closure works anywhere a validator is expected.
|
||||||
|
// Combine validators with [All] (stop at the first failure) or [Collect]
|
||||||
|
// (run them all and join every failure).
|
||||||
|
//
|
||||||
|
// username := validate.All(
|
||||||
|
// validate.MinLength(3),
|
||||||
|
// validate.MaxLength(16),
|
||||||
|
// validate.Chars("abcdefghijklmnopqrstuvwxyz0123456789_"),
|
||||||
|
// )
|
||||||
|
// if err := username(input); err != nil {
|
||||||
|
// // errors.Is(err, validate.ErrDisallowedChars) still works here
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Errors are plain sentinel values (see [Error]); match them with
|
||||||
|
// errors.Is. [Err] matches any error produced by this package.
|
||||||
|
//
|
||||||
|
// # Comparison with other packages
|
||||||
|
//
|
||||||
|
// - go-playground/validator drives validation from struct tags and
|
||||||
|
// reflection.
|
||||||
|
// - go-ozzo/ozzo-validation composes rules in code, but through a Rule
|
||||||
|
// interface over interface{}.
|
||||||
|
// - asaskevich/govalidator is a bag of IsX string helpers.
|
||||||
|
//
|
||||||
|
// This package is the code-composition style with two constraints kept
|
||||||
|
// deliberately: validators are ordinary generic functions, checked by the
|
||||||
|
// compiler with no reflection ([In], [Equal] and the rest will not accept
|
||||||
|
// the wrong type), and there are no dependencies outside the standard
|
||||||
|
// library.
|
||||||
|
//
|
||||||
|
// # When to use something else
|
||||||
|
//
|
||||||
|
// Reach for go-playground/validator if you want struct-tag validation,
|
||||||
|
// translated messages, or a large catalogue of built-in checks. This
|
||||||
|
// package stays small and leaves struct/field wiring and error
|
||||||
|
// presentation to the caller.
|
||||||
|
package validate
|
||||||
@@ -2,10 +2,9 @@ package validate
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Validation error.
|
// Err is the zero Error. It carries no message, so errors.Is(x, Err) is
|
||||||
var (
|
// true for any error produced by this package.
|
||||||
Err = Error{}
|
var Err = Error{}
|
||||||
)
|
|
||||||
|
|
||||||
// Error represents a validation error.
|
// Error represents a validation error.
|
||||||
type Error struct {
|
type Error struct {
|
||||||
@@ -13,8 +12,9 @@ type Error struct {
|
|||||||
Data []any
|
Data []any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error retrieves the message of a validation Error.
|
// Error returns the error message. If Data is non-empty, Message is used
|
||||||
// If it has Data, the message will be formatted.
|
// as an fmt.Sprintf format string and Data as its arguments (this is how
|
||||||
|
// the sentinels with %d/%q verbs are filled in, e.g. via With).
|
||||||
func (e Error) Error() string {
|
func (e Error) Error() string {
|
||||||
if len(e.Data) > 0 {
|
if len(e.Data) > 0 {
|
||||||
return fmt.Sprintf(e.Message, e.Data...)
|
return fmt.Sprintf(e.Message, e.Data...)
|
||||||
@@ -34,6 +34,10 @@ func (e Error) Is(target error) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With returns a copy of the Error with value appended to Data, so it
|
||||||
|
// lands in the message when Message contains a formatting verb:
|
||||||
|
//
|
||||||
|
// ErrMustBeLonger.With(4) // "must contain at least 4 characters"
|
||||||
func (e Error) With(value any) Error {
|
func (e Error) With(value any) Error {
|
||||||
if e.Data == nil {
|
if e.Data == nil {
|
||||||
e.Data = []any{}
|
e.Data = []any{}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
package validate_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.aneur.in/go/validate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A validator is any func(T) error. Compose the built-ins with your own
|
||||||
|
// closures using All (stop at the first failure) or Collect (report all).
|
||||||
|
func Example() {
|
||||||
|
username := validate.All(
|
||||||
|
validate.MinLength(3),
|
||||||
|
validate.MaxLength(16),
|
||||||
|
validate.Chars("abcdefghijklmnopqrstuvwxyz0123456789_"),
|
||||||
|
func(s string) error {
|
||||||
|
if strings.HasPrefix(s, "_") {
|
||||||
|
return errors.New("must not start with an underscore")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(username("ok_name"))
|
||||||
|
fmt.Println(username("_nope"))
|
||||||
|
fmt.Println(username("Nope"))
|
||||||
|
// Output:
|
||||||
|
// <nil>
|
||||||
|
// must not start with an underscore
|
||||||
|
// contains disallowed characters
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect runs every validator and joins the failures; errors.Is still
|
||||||
|
// matches each one.
|
||||||
|
func Example_collectAll() {
|
||||||
|
password := validate.Collect(
|
||||||
|
validate.MinLength(8),
|
||||||
|
validate.Chars("abcdefghijklmnopqrstuvwxyz"),
|
||||||
|
)
|
||||||
|
|
||||||
|
err := password("Ab1")
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println(errors.Is(err, validate.ErrMustBeLonger))
|
||||||
|
fmt.Println(errors.Is(err, validate.ErrDisallowedChars))
|
||||||
|
// Output:
|
||||||
|
// must contain at least 8 characters
|
||||||
|
// contains disallowed characters
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errors are plain sentinels, so errors.Is works with no library-specific
|
||||||
|
// machinery. Err matches any error from this package.
|
||||||
|
func Example_errorsIs() {
|
||||||
|
err := validate.Email("not-an-email")
|
||||||
|
|
||||||
|
fmt.Println(err)
|
||||||
|
fmt.Println(errors.Is(err, validate.ErrInvalidEmail))
|
||||||
|
fmt.Println(errors.Is(err, validate.Err))
|
||||||
|
// Output:
|
||||||
|
// invalid email address
|
||||||
|
// true
|
||||||
|
// true
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ var (
|
|||||||
ErrValueNotAllowed = NewError("not allowed")
|
ErrValueNotAllowed = NewError("not allowed")
|
||||||
)
|
)
|
||||||
|
|
||||||
// In validates whether a value is found in a slice of allowed values.
|
// In validates that a value equals one of the allowed values.
|
||||||
func In[T comparable](allow ...T) func(T) error {
|
func In[T comparable](allow ...T) func(T) error {
|
||||||
return func(value T) error {
|
return func(value T) error {
|
||||||
for _, cmp := range allow {
|
for _, cmp := range allow {
|
||||||
@@ -16,10 +16,10 @@ func In[T comparable](allow ...T) func(T) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NotIn validates whether a value is not found in a slice of disallowed values.
|
// NotIn validates that a value equals none of the disallowed values.
|
||||||
func NotIn[T comparable](allow ...T) func(T) error {
|
func NotIn[T comparable](disallow ...T) func(T) error {
|
||||||
return func(value T) error {
|
return func(value T) error {
|
||||||
for _, cmp := range allow {
|
for _, cmp := range disallow {
|
||||||
if cmp == value {
|
if cmp == value {
|
||||||
return ErrValueNotAllowed
|
return ErrValueNotAllowed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,57 @@
|
|||||||
package validate
|
package validate
|
||||||
|
|
||||||
|
import "unicode/utf8"
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrMustBeLonger = NewError("must contain at least %d characters")
|
ErrMustBeLonger = NewError("must contain at least %d characters")
|
||||||
ErrMustBeShorter = NewError("must contain no more than %d characters")
|
ErrMustBeShorter = NewError("must contain no more than %d characters")
|
||||||
|
ErrMustHaveMoreBytes = NewError("must have at least %d bytes")
|
||||||
|
ErrMustHaveFewerBytes = NewError("must have no more than %d bytes")
|
||||||
)
|
)
|
||||||
|
|
||||||
// MaxLength validates the length of a string as being less than or equal to a given maximum.
|
// MaxLength validates that a string is no longer than a given maximum.
|
||||||
|
// Length is counted in runes, so multi-byte characters count as one.
|
||||||
func MaxLength(l int) func(string) error {
|
func MaxLength(l int) func(string) error {
|
||||||
return func(value string) error {
|
return func(value string) error {
|
||||||
if len(value) > l {
|
if utf8.RuneCountInString(value) > l {
|
||||||
return ErrMustBeShorter.With(l)
|
return ErrMustBeShorter.With(l)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MinLength validates the length of a string as being greater than or equal to a given minimum.
|
// MinLength validates that a string is at least a given minimum length.
|
||||||
|
// Length is counted in runes, so multi-byte characters count as one.
|
||||||
func MinLength(l int) func(string) error {
|
func MinLength(l int) func(string) error {
|
||||||
return func(value string) error {
|
return func(value string) error {
|
||||||
if len(value) < l {
|
if utf8.RuneCountInString(value) < l {
|
||||||
return ErrMustBeLonger.With(l)
|
return ErrMustBeLonger.With(l)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MaxLengthBytes validates that a string is no longer than a given maximum
|
||||||
|
// number of bytes (len). Prefer [MaxLength] for a limit on visible
|
||||||
|
// characters; use this when the budget is genuinely a byte count, such as
|
||||||
|
// a fixed-width column or a wire-format field.
|
||||||
|
func MaxLengthBytes(l int) func(string) error {
|
||||||
|
return func(value string) error {
|
||||||
|
if len(value) > l {
|
||||||
|
return ErrMustHaveFewerBytes.With(l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MinLengthBytes validates that a string is at least a given minimum
|
||||||
|
// number of bytes (len). See [MaxLengthBytes] on when to prefer this over
|
||||||
|
// [MinLength].
|
||||||
|
func MinLengthBytes(l int) func(string) error {
|
||||||
|
return func(value string) error {
|
||||||
|
if len(value) < l {
|
||||||
|
return ErrMustHaveMoreBytes.With(l)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+82
-2
@@ -20,7 +20,14 @@ func ExampleMinLength() {
|
|||||||
|
|
||||||
func TestMaxLength(t *testing.T) {
|
func TestMaxLength(t *testing.T) {
|
||||||
testCases := map[int]map[string]error{
|
testCases := map[int]map[string]error{
|
||||||
8: {"abcd": nil, "abcdefgh": nil, "abcd efg": nil, "abcdefghi": ErrMustBeShorter.With(8)},
|
8: {
|
||||||
|
"abcd": nil,
|
||||||
|
"abcdefgh": nil,
|
||||||
|
"abcd efg": nil,
|
||||||
|
"abcdéfgh": nil, // 8 runes, 9 bytes
|
||||||
|
"abcdefghi": ErrMustBeShorter.With(8),
|
||||||
|
"abcdéfghi": ErrMustBeShorter.With(8), // 9 runes, 10 bytes
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for setup, values := range testCases {
|
for setup, values := range testCases {
|
||||||
@@ -41,7 +48,14 @@ func TestMaxLength(t *testing.T) {
|
|||||||
|
|
||||||
func TestMinLength(t *testing.T) {
|
func TestMinLength(t *testing.T) {
|
||||||
testCases := map[int]map[string]error{
|
testCases := map[int]map[string]error{
|
||||||
8: {"abcd": ErrMustBeLonger.With(8), "abcdefgh": nil, "abcd efg": nil, "abcdefghi": nil},
|
8: {
|
||||||
|
"abcd": ErrMustBeLonger.With(8),
|
||||||
|
"abcdéfg": ErrMustBeLonger.With(8), // 7 runes, 8 bytes
|
||||||
|
"abcdefgh": nil,
|
||||||
|
"abcdéfgh": nil, // 8 runes, 9 bytes
|
||||||
|
"abcd efg": nil,
|
||||||
|
"abcdefghi": nil,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for setup, values := range testCases {
|
for setup, values := range testCases {
|
||||||
@@ -59,3 +73,69 @@ func TestMinLength(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExampleMaxLengthBytes() {
|
||||||
|
testMaxLengthBytes := MaxLengthBytes(8)
|
||||||
|
fmt.Println(testMaxLengthBytes("cafés round the world"))
|
||||||
|
// Output: must have no more than 8 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleMinLengthBytes() {
|
||||||
|
testMinLengthBytes := MinLengthBytes(8)
|
||||||
|
fmt.Println(testMinLengthBytes("2short"))
|
||||||
|
// Output: must have at least 8 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMaxLengthBytes(t *testing.T) {
|
||||||
|
testCases := map[int]map[string]error{
|
||||||
|
8: {
|
||||||
|
"abcd": nil,
|
||||||
|
"abcdefgh": nil,
|
||||||
|
"abcdéfg": nil, // 7 runes, 8 bytes
|
||||||
|
"abcdéfgh": ErrMustHaveFewerBytes.With(8), // 8 runes, 9 bytes
|
||||||
|
"abcdefghi": ErrMustHaveFewerBytes.With(8),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for setup, values := range testCases {
|
||||||
|
testMaxLengthBytes := MaxLengthBytes(setup)
|
||||||
|
|
||||||
|
for input, want := range values {
|
||||||
|
t.Run(fmt.Sprintf("%d/%s", setup, input), func(t *testing.T) {
|
||||||
|
got := testMaxLengthBytes(input)
|
||||||
|
|
||||||
|
if !errors.Is(got, want) {
|
||||||
|
t.Error("got", got)
|
||||||
|
t.Error("want", want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMinLengthBytes(t *testing.T) {
|
||||||
|
testCases := map[int]map[string]error{
|
||||||
|
8: {
|
||||||
|
"abcd": ErrMustHaveMoreBytes.With(8),
|
||||||
|
"abcdefg": ErrMustHaveMoreBytes.With(8),
|
||||||
|
"abcdéf": ErrMustHaveMoreBytes.With(8), // 6 runes, 7 bytes
|
||||||
|
"abcdéfg": nil, // 7 runes, 8 bytes
|
||||||
|
"abcdefgh": nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for setup, values := range testCases {
|
||||||
|
testMinLengthBytes := MinLengthBytes(setup)
|
||||||
|
|
||||||
|
for input, want := range values {
|
||||||
|
t.Run(fmt.Sprintf("%d/%s", setup, input), func(t *testing.T) {
|
||||||
|
got := testMinLengthBytes(input)
|
||||||
|
|
||||||
|
if !errors.Is(got, want) {
|
||||||
|
t.Error("got", got)
|
||||||
|
t.Error("want", want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Max validates whether an integer is less than or equal to a given maximum.
|
// Max validates whether an integer is less than or equal to a given maximum.
|
||||||
// If exclusive is true, an equal value will also produce an error.
|
// If exclusive is true, an equal value also produces an error.
|
||||||
func Max(n int, exclusive bool) func(int) error {
|
func Max(n int, exclusive bool) func(int) error {
|
||||||
return func(value int) error {
|
return func(value int) error {
|
||||||
if exclusive {
|
if exclusive {
|
||||||
@@ -24,7 +24,7 @@ func Max(n int, exclusive bool) func(int) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MaxFloat32 validates whether a float32 is less than or equal to a given maximum.
|
// MaxFloat32 validates whether a float32 is less than or equal to a given maximum.
|
||||||
// If exclusive is true, an equal value will also produce an error.
|
// If exclusive is true, an equal value also produces an error.
|
||||||
func MaxFloat32(n float32, exclusive bool) func(float32) error {
|
func MaxFloat32(n float32, exclusive bool) func(float32) error {
|
||||||
return func(value float32) error {
|
return func(value float32) error {
|
||||||
if exclusive {
|
if exclusive {
|
||||||
@@ -40,7 +40,7 @@ func MaxFloat32(n float32, exclusive bool) func(float32) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// MaxFloat64 validates whether a float64 is less than or equal to a given maximum.
|
// MaxFloat64 validates whether a float64 is less than or equal to a given maximum.
|
||||||
// If exclusive is true, an equal value will also produce an error.
|
// If exclusive is true, an equal value also produces an error.
|
||||||
func MaxFloat64(n float64, exclusive bool) func(float64) error {
|
func MaxFloat64(n float64, exclusive bool) func(float64) error {
|
||||||
return func(value float64) error {
|
return func(value float64) error {
|
||||||
if exclusive {
|
if exclusive {
|
||||||
@@ -55,8 +55,8 @@ func MaxFloat64(n float64, exclusive bool) func(float64) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Min validates whether an integer is less than or equal to a given maximum.
|
// Min validates whether an integer is greater than or equal to a given minimum.
|
||||||
// If exclusive is true, an equal value will also produce an error.
|
// If exclusive is true, an equal value also produces an error.
|
||||||
func Min(n int, exclusive bool) func(int) error {
|
func Min(n int, exclusive bool) func(int) error {
|
||||||
return func(value int) error {
|
return func(value int) error {
|
||||||
if exclusive {
|
if exclusive {
|
||||||
@@ -71,8 +71,8 @@ func Min(n int, exclusive bool) func(int) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MinFloat32 validates whether a float32 is less than or equal to a given maximum.
|
// MinFloat32 validates whether a float32 is greater than or equal to a given minimum.
|
||||||
// If exclusive is true, an equal value will also produce an error.
|
// If exclusive is true, an equal value also produces an error.
|
||||||
func MinFloat32(n float32, exclusive bool) func(float32) error {
|
func MinFloat32(n float32, exclusive bool) func(float32) error {
|
||||||
return func(value float32) error {
|
return func(value float32) error {
|
||||||
if exclusive {
|
if exclusive {
|
||||||
@@ -87,8 +87,8 @@ func MinFloat32(n float32, exclusive bool) func(float32) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MinFloat64 validates whether a float64 is less than or equal to a given maximum.
|
// MinFloat64 validates whether a float64 is greater than or equal to a given minimum.
|
||||||
// If exclusive is true, an equal value will also produce an error.
|
// If exclusive is true, an equal value also produces an error.
|
||||||
func MinFloat64(n float64, exclusive bool) func(float64) error {
|
func MinFloat64(n float64, exclusive bool) func(float64) error {
|
||||||
return func(value float64) error {
|
return func(value float64) error {
|
||||||
if exclusive {
|
if exclusive {
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ var (
|
|||||||
ErrInvalidURL Error = NewError("invalid URL")
|
ErrInvalidURL Error = NewError("invalid URL")
|
||||||
)
|
)
|
||||||
|
|
||||||
// URL validates a URL.
|
// URL validates that a string is an absolute URL (with a scheme) or an
|
||||||
|
// absolute path, per net/url.ParseRequestURI. "example.com" with no
|
||||||
|
// scheme is rejected.
|
||||||
func URL(value string) error {
|
func URL(value string) error {
|
||||||
if _, err := url.ParseRequestURI(value); err != nil {
|
if _, err := url.ParseRequestURI(value); err != nil {
|
||||||
return ErrInvalidURL
|
return ErrInvalidURL
|
||||||
|
|||||||
@@ -10,8 +10,9 @@ var (
|
|||||||
|
|
||||||
var uuidRegexp = regexp.MustCompile("^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$")
|
var uuidRegexp = regexp.MustCompile("^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$")
|
||||||
|
|
||||||
// UUID validates a UUID string.
|
// UUID validates a UUID string in the canonical 8-4-4-4-12 hyphenated
|
||||||
// The UUID must be formatted with separators.
|
// form. Only lowercase hexadecimal is accepted; the version and variant
|
||||||
|
// bits are not checked.
|
||||||
func UUID(value string) error {
|
func UUID(value string) error {
|
||||||
if !uuidRegexp.MatchString(value) {
|
if !uuidRegexp.MatchString(value) {
|
||||||
return ErrInvalidUUID
|
return ErrInvalidUUID
|
||||||
|
|||||||
Reference in New Issue
Block a user