docs: explain what distinguishes validate (+ comment audit) #11

Merged
aneurin merged 4 commits from docs-positioning into main 2026-09-07 16:35:55 +00:00
9 changed files with 38 additions and 28 deletions
Showing only changes of commit da971f4df5 - Show all commits
+3 -2
View File
@@ -1,7 +1,8 @@
package validate
// All validates a value using a sequence of validation functions.
// If any validation function returns an error, the sequence stops and the error is returned.
// All validates a value against a sequence of validation functions,
// 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 {
return func(value T) error {
for _, f := range fs {
+1 -1
View File
@@ -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 {
return func(value string) error {
for _, r := range disallow {
+1 -1
View File
@@ -13,7 +13,7 @@ func ExampleChars() {
}
func ExampleExceptChars() {
testExceptChars := Chars("0123456789abcdef")
testExceptChars := ExceptChars("0123456789abcdef")
fmt.Println(testExceptChars("invalid input"))
// Output: contains disallowed characters
}
+10 -6
View File
@@ -2,10 +2,9 @@ package validate
import "fmt"
// Validation error.
var (
Err = Error{}
)
// Err is the zero Error. It carries no message, so errors.Is(x, Err) is
// true for any error produced by this package.
var Err = Error{}
// Error represents a validation error.
type Error struct {
@@ -13,8 +12,9 @@ type Error struct {
Data []any
}
// Error retrieves the message of a validation Error.
// If it has Data, the message will be formatted.
// Error returns the error message. If Data is non-empty, Message is used
// 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 {
if len(e.Data) > 0 {
return fmt.Sprintf(e.Message, e.Data...)
@@ -34,6 +34,10 @@ func (e Error) Is(target error) bool {
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 {
if e.Data == nil {
e.Data = []any{}
+4 -4
View File
@@ -4,7 +4,7 @@ var (
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 {
return func(value T) error {
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.
func NotIn[T comparable](allow ...T) func(T) error {
// NotIn validates that a value equals none of the disallowed values.
func NotIn[T comparable](disallow ...T) func(T) error {
return func(value T) error {
for _, cmp := range allow {
for _, cmp := range disallow {
if cmp == value {
return ErrValueNotAllowed
}
+4 -2
View File
@@ -5,7 +5,8 @@ var (
ErrMustBeShorter = NewError("must contain no more than %d characters")
)
// 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 measured in bytes (len), not runes.
Outdated
Review

The original intention was to measure text length. Does this make it more correct to measure runes rather than bytes?

The original intention was to measure text length. Does this make it more correct to measure runes rather than bytes?
func MaxLength(l int) func(string) error {
return func(value string) error {
if len(value) > l {
@@ -15,7 +16,8 @@ func MaxLength(l int) func(string) error {
}
}
// 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 measured in bytes (len), not runes.
func MinLength(l int) func(string) error {
return func(value string) error {
if len(value) < l {
+9 -9
View File
@@ -8,7 +8,7 @@ var (
)
// 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 {
return func(value int) error {
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.
// 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 {
return func(value float32) error {
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.
// 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 {
return func(value float64) error {
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.
// If exclusive is true, an equal value will also produce an error.
// Min validates whether an integer is greater than or equal to a given minimum.
// If exclusive is true, an equal value also produces an error.
func Min(n int, exclusive bool) func(int) error {
return func(value int) error {
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.
// If exclusive is true, an equal value will also produce an error.
// MinFloat32 validates whether a float32 is greater than or equal to a given minimum.
// If exclusive is true, an equal value also produces an error.
func MinFloat32(n float32, exclusive bool) func(float32) error {
return func(value float32) error {
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.
// If exclusive is true, an equal value will also produce an error.
// MinFloat64 validates whether a float64 is greater than or equal to a given minimum.
// If exclusive is true, an equal value also produces an error.
func MinFloat64(n float64, exclusive bool) func(float64) error {
return func(value float64) error {
if exclusive {
+3 -1
View File
@@ -6,7 +6,9 @@ var (
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 {
if _, err := url.ParseRequestURI(value); err != nil {
return ErrInvalidURL
+3 -2
View File
@@ -10,8 +10,9 @@ var (
var uuidRegexp = regexp.MustCompile("^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$")
// UUID validates a UUID string.
// The UUID must be formatted with separators.
// UUID validates a UUID string in the canonical 8-4-4-4-12 hyphenated
// form. Only lowercase hexadecimal is accepted; the version and variant
// bits are not checked.
func UUID(value string) error {
if !uuidRegexp.MatchString(value) {
return ErrInvalidUUID