Skip to content

[3/11] Add omitempty checker-tag modifier - #226

Merged
cinar merged 2 commits into
mainfrom
p1-03-omitempty
Sep 5, 2026
Merged

[3/11] Add omitempty checker-tag modifier#226
cinar merged 2 commits into
mainfrom
p1-03-omitempty

Conversation

@cinar

@cinar cinar commented Sep 5, 2026

Copy link
Copy Markdown
Owner

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 omitempty as a checkers-tag pipeline modifier (not a registered checker in the makers map, since it needs the runtime value, not just its own static parameters):

type Profile struct {
	Website string `checkers:"omitempty url"`
}
  • reflectCheckFieldWithConfig strips omitempty out of the config string (extractOmitEmpty) and, if the field's own value IsZero(), skips the rest of that field's checks entirely instead of running them.
  • It looks at the field's original value, not one already transformed by an earlier normalizer in the same tag — trim omitempty required on " " still fails required after trimming, since a whitespace-only string isn't the zero value to begin with.
  • With the @ 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.
  • JSONSchema now treats omitempty like a normalizer — skipped rather than recorded in XChecker — since it's a pipeline instruction, not a shape constraint.
  • Documented in the README (new "Optional Fields with omitempty" section), including the omitempty+required contradiction: omitempty wins, so required never runs on a zero value — the two shouldn't be paired.

Testing

  • New omit_empty_test.go: skip-on-zero, still-checks-on-non-zero, valid-non-zero, the documented omitempty required no-op, the "looks at original value" ordering case, @omitempty on a slice container (empty and non-empty), and CheckWithConfig.
  • Extended TestJSONSchemaIgnoresNormalizers to cover omitempty.
  • Full suite passes, 100% coverage maintained.
  • go vet, gosec, revive clean.

Fixes #200


Chain note: PR 3 of 11 — stacked on p1-02-error-template-cache (#225), which is itself stacked on the now-merged p1-01-regexp-cache (#224). Merge in order.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FLdVmP5daHiknrTW4Geh2i

cinar and others added 2 commits September 5, 2026 16:56
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
@cinar cinar mentioned this pull request Sep 5, 2026
Base automatically changed from p1-02-error-template-cache to main September 5, 2026 17:05
@cinar
cinar merged commit bc94b67 into main Sep 5, 2026
@cinar
cinar deleted the p1-03-omitempty branch September 5, 2026 17:06
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add omitempty struct-tag modifier to skip validation on zero values

1 participant