docs: positioning README, package doc, usage examples
CI / test (pull_request) Successful in 43s

Closes #3.

- doc.go: package overview -- what a validator is, All vs Collect, errors
  are sentinels, how this compares to validator/v10 (tags+reflection),
  ozzo-validation (Rule interface) and govalidator (IsX helpers), and when
  to pick something else.
- README.md: rewritten from three lines to a positioning intro, a
  "why this and not X" table, a building-blocks table, and a
  "not a fit if" note.
- example_test.go: runnable Example (compose built-ins + a closure),
  Example_collectAll (errors.Is over a joined error), Example_errorsIs
  (sentinel matching, Err as match-anything).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-07 14:01:03 +01:00
co-authored by Claude Sonnet 5
parent da971f4df5
commit 4483105a78
3 changed files with 187 additions and 2 deletions
+66
View File
@@ -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
}