[3/11] Add omitempty checker-tag modifier - #226
Merged
Merged
Conversation
ErrorWithLocale parsed the localized message string into a
*template.Template on every single call, even though the large
majority of messages ("Required value is missing.", "Not a valid
email address.", ...) contain no {{ }} placeholders at all and don't
need template parsing or execution in the first place.
Add a fast path: if the message contains no "{{", return it directly.
For the messages that do have placeholders, parse once and cache the
*template.Template keyed by the message string (errorTemplateCache,
same pattern as regexpCache from #198) instead of re-parsing on every
call.
Fixes #199
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i
Optional fields that must still be valid when present required a workaround: either building the struct conditionally, or leaning on required-if-style tricks. Every major struct-tag validator has a shorthand for this; Checker didn't. Add omitempty as a tag pipeline modifier, not a registered checker: reflectCheckFieldWithConfig strips it out of the config string (extractOmitEmpty) and, if the field's own value IsZero(), skips running the rest of that field's checks entirely. It looks at the field's original value, not one already transformed by an earlier normalizer in the same tag, so "trim omitempty required" on whitespace-only input still fails required after trimming. With the "@" prefix it applies to a slice/map container instead of its items, following the same convention "@" already uses everywhere else in the tag DSL. JSONSchema now treats it like a normalizer (skipped rather than recorded in XChecker), since it's a pipeline instruction, not a shape constraint. Documented in the README, including the documented omitempty+required contradiction (omitempty wins: required never runs on a zero value). Fixes #200 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i
Merged
cinar
added a commit
that referenced
this pull request
Sep 5, 2026
## Problem UUIDs are one of the most common identifier formats in modern APIs, and validating one currently requires a custom `RegisterMaker` call — a common enough gap that every source report flagged it as a top adoption blocker. ## Fix Add `uuid` as a built-in checker, following the established per-checker file pattern (see `email.go`): - `IsUUID`/`checkUUID`/`makeUUID`, reusing `IsRegexp` (cached since #198) rather than hand-rolling a match. - Accepts any UUID version/variant: 32 hex digits grouped 8-4-4-4-12, case-insensitive, per RFC 4122 — not narrowed to v4 only, since the tag is just `uuid` with no version parameter. - Registered in `maker.go`'s `makers` map. - `schema_maker.go` maps it to JSON Schema's `"format": "uuid"`. - `NOT_UUID` message added to all 23 locale files plus en-US, verified against `locales_test.go`'s placeholder-parity check. - Documented in the README's checker table. ## Testing - `uuid_test.go`: valid/invalid, uppercase, wrong-grouping, non-string-field panic (existing contract), struct-tag valid/invalid. - `TestJSONSchemaUUIDFormat` for the schema mapping. - Full suite passes, 100% coverage maintained. - `go vet`, `gosec`, `revive` clean. `gofmt -l` clean on every touched file. Fixes #201 --- **Chain note:** PR 4 of 11 — stacked on `p1-03-omitempty` (#226). 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i --------- Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Optional fields that must still be valid when present currently require a workaround — building the struct conditionally, or leaning on
required-if-style tricks. Every comparable struct-tag validator has a shorthand for "skip validation if this field is empty/zero, otherwise validate normally"; Checker didn't.Fix
Add
omitemptyas a checkers-tag pipeline modifier (not a registered checker in themakersmap, since it needs the runtime value, not just its own static parameters):reflectCheckFieldWithConfigstripsomitemptyout of the config string (extractOmitEmpty) and, if the field's own valueIsZero(), skips the rest of that field's checks entirely instead of running them.trim omitempty requiredon" "still failsrequiredafter trimming, since a whitespace-only string isn't the zero value to begin with.@prefix it applies to a slice/map container instead of its items (@omitempty @min-len:1), following the same convention@already uses everywhere else in the tag DSL.JSONSchemanow treatsomitemptylike a normalizer — skipped rather than recorded inXChecker— since it's a pipeline instruction, not a shape constraint.omitempty" section), including theomitempty+requiredcontradiction:omitemptywins, sorequirednever runs on a zero value — the two shouldn't be paired.Testing
omit_empty_test.go: skip-on-zero, still-checks-on-non-zero, valid-non-zero, the documentedomitempty requiredno-op, the "looks at original value" ordering case,@omitemptyon a slice container (empty and non-empty), andCheckWithConfig.TestJSONSchemaIgnoresNormalizersto coveromitempty.go vet,gosec,reviveclean.Fixes #200
Chain note: PR 3 of 11 — stacked on
p1-02-error-template-cache(#225), which is itself stacked on the now-mergedp1-01-regexp-cache(#224). Merge in order.🤖 Generated with Claude Code
https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i