doc.go — package overview: what a validator is, All vs Collect, errors-as-sentinels, a comparison with validator/v10 (tags + reflection), ozzo-validation (Rule interface) and govalidator (IsX helpers), and a "when to use something else".
README.md — rewritten from three lines: positioning intro, "why this and not X" table, building-blocks table, "not a fit if" note.
example_test.go — runnable Example (compose built-ins + an inline closure), Example_collectAll (errors.Is over a joined error), Example_errorsIs (sentinel matching).
gofmt / go vet ./... / go test ./... all green; every example has verified output.
Closes #3.
Per the note on the issue, this starts with a comment audit, then does the docs.
### Commit 1 — `fix incorrect and vague doc comments`
Comments only, no behaviour change.
- **`number.go`** — `Min`, `MinFloat32`, `MinFloat64` all said *"less than or equal to a given maximum"* (copy-pasted from the `Max` family). They check the minimum.
- **`chars_test.go`** — `ExampleExceptChars` called `Chars`, not `ExceptChars`, so it never exercised the function it documents.
- **`chars.go`** — "does not contain disallowed characters" was circular.
- **`in.go`** — `NotIn`'s parameter was named `allow`; renamed to `disallow`.
- **`length.go`** — noted length is bytes (`len`), not runes (the `%d characters` messages imply otherwise).
- **`url.go`** — spelled out that `ParseRequestURI` wants an absolute URL or path (`"example.com"` fails).
- **`uuid.go`** — noted lowercase-hex only, version/variant not checked.
- **`error.go`** — documented `Err` (match-anything sentinel), how `Error()` formats with `Data`, and what `With` does. **`all.go`** points at `Collect`.
### Commit 2 — `docs: positioning README, package doc, usage examples`
- **`doc.go`** — package overview: what a validator is, `All` vs `Collect`, errors-as-sentinels, a comparison with `validator/v10` (tags + reflection), `ozzo-validation` (`Rule` interface) and `govalidator` (`IsX` helpers), and a "when to use something else".
- **`README.md`** — rewritten from three lines: positioning intro, "why this and not X" table, building-blocks table, "not a fit if" note.
- **`example_test.go`** — runnable `Example` (compose built-ins + an inline closure), `Example_collectAll` (`errors.Is` over a joined error), `Example_errorsIs` (sentinel matching).
`gofmt` / `go vet ./...` / `go test ./...` all green; every example has verified output.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Audit ahead of the README/doc.go work (issue #3):
- number.go: Min, MinFloat32, MinFloat64 all said "less than or equal to a
given maximum" -- copy-pasted from the Max family. They check the
minimum. Fixed.
- chars_test.go: ExampleExceptChars called Chars, not ExceptChars, so it
never exercised the function it documents.
- chars.go: "does not contain disallowed characters" was circular.
- in.go: NotIn's parameter was named "allow"; renamed to "disallow".
- length.go: note that length is bytes (len), not runes -- the "%d
characters" messages imply otherwise.
- url.go: spell out that ParseRequestURI wants an absolute URL or path.
- uuid.go: note lowercase-hex only, version/variant not checked.
- error.go: explain Err (the match-anything sentinel), how Error() formats
with Data, and what With does. all.go: point at Collect.
Comments only; no behaviour change. go vet + go test ./... pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
MinLength/MaxLength measured len(value), so a string of 8 accented
characters (9+ bytes) failed MaxLength(8) despite the error message
promising "characters". Count with utf8.RuneCountInString so the check
matches the wording and the common-sense intent.
Still not grapheme-cluster aware (combining marks, emoji ZWJ sequences
count as several runes), which is out of scope for a stdlib-only helper.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Good call — switched MinLength/MaxLength to count runes via utf8.RuneCountInString in 4e2e05c, so the check now matches the "characters" wording. Added test cases with accented input that is within the rune limit but over the byte limit. Still stdlib-only; grapheme clusters (combining marks, emoji ZWJ sequences) still count as multiple runes, which I noted in the commit message as out of scope.
Good call — switched `MinLength`/`MaxLength` to count runes via `utf8.RuneCountInString` in 4e2e05c, so the check now matches the "characters" wording. Added test cases with accented input that is within the rune limit but over the byte limit. Still stdlib-only; grapheme clusters (combining marks, emoji ZWJ sequences) still count as multiple runes, which I noted in the commit message as out of scope.
Now that Min/MaxLength count runes, we are lacking a byte length validator, which may still be useful.
Add MaxLengthBytes and MinLengthBytes to fill the gap.
Now that Min/MaxLength count runes, we are lacking a byte length validator, which may still be useful.
Add MaxLengthBytes and MinLengthBytes to fill the gap.
MinLength/MaxLength now count runes, leaving no way to bound a string by
its byte size -- still wanted for fixed-width columns and wire-format
fields. Add the byte-counting pair alongside them, with their own "%d
bytes" sentinels (ErrMustHaveMoreBytes / ErrMustHaveFewerBytes).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Added MinLengthBytes / MaxLengthBytes in 164dcbf, with their own ErrMustHaveMoreBytes / ErrMustHaveFewerBytes sentinels (%d bytes wording) and tests using multibyte input where the byte and rune counts diverge. Doc comments cross-reference the rune-counting pair and vice versa; README building-blocks row updated.
Added `MinLengthBytes` / `MaxLengthBytes` in 164dcbf, with their own `ErrMustHaveMoreBytes` / `ErrMustHaveFewerBytes` sentinels (`%d bytes` wording) and tests using multibyte input where the byte and rune counts diverge. Doc comments cross-reference the rune-counting pair and vice versa; README building-blocks row updated.
claude
requested review from aneurin 2026-09-07 15:48:48 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #3.
Per the note on the issue, this starts with a comment audit, then does the docs.
Commit 1 —
fix incorrect and vague doc commentsComments only, no behaviour change.
number.go—Min,MinFloat32,MinFloat64all said "less than or equal to a given maximum" (copy-pasted from theMaxfamily). They check the minimum.chars_test.go—ExampleExceptCharscalledChars, notExceptChars, so it never exercised the function it documents.chars.go— "does not contain disallowed characters" was circular.in.go—NotIn's parameter was namedallow; renamed todisallow.length.go— noted length is bytes (len), not runes (the%d charactersmessages imply otherwise).url.go— spelled out thatParseRequestURIwants an absolute URL or path ("example.com"fails).uuid.go— noted lowercase-hex only, version/variant not checked.error.go— documentedErr(match-anything sentinel), howError()formats withData, and whatWithdoes.all.gopoints atCollect.Commit 2 —
docs: positioning README, package doc, usage examplesdoc.go— package overview: what a validator is,AllvsCollect, errors-as-sentinels, a comparison withvalidator/v10(tags + reflection),ozzo-validation(Ruleinterface) andgovalidator(IsXhelpers), and a "when to use something else".README.md— rewritten from three lines: positioning intro, "why this and not X" table, building-blocks table, "not a fit if" note.example_test.go— runnableExample(compose built-ins + an inline closure),Example_collectAll(errors.Isover a joined error),Example_errorsIs(sentinel matching).gofmt/go vet ./.../go test ./...all green; every example has verified output.🤖 Generated with Claude Code
@@ -7,2 +7,3 @@// 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.The original intention was to measure text length. Does this make it more correct to measure runes rather than bytes?
Good call — switched
MinLength/MaxLengthto count runes viautf8.RuneCountInStringin4e2e05c, so the check now matches the "characters" wording. Added test cases with accented input that is within the rune limit but over the byte limit. Still stdlib-only; grapheme clusters (combining marks, emoji ZWJ sequences) still count as multiple runes, which I noted in the commit message as out of scope.Now that Min/MaxLength count runes, we are lacking a byte length validator, which may still be useful.
Add MaxLengthBytes and MinLengthBytes to fill the gap.
Added
MinLengthBytes/MaxLengthBytesin164dcbf, with their ownErrMustHaveMoreBytes/ErrMustHaveFewerBytessentinels (%d byteswording) and tests using multibyte input where the byte and rune counts diverge. Doc comments cross-reference the rune-counting pair and vice versa; README building-blocks row updated.