Repository navigation
Releases: microbus-io/dv8
Release list
Compiled execution plans and tag introspection
Committed (24a5a6c). Release notes for v0.4.0:
Validation is now compiled once per type into a cached execution plan and executed as pre-bound closures, instead of re-parsing directive strings on every call. All operands are resolved at compile time: length and value bounds, defaults, oneof sets, and regular expressions (whose cache lock is gone from the hot path), while untagged fields and no-op containers are pruned from the plan entirely. Behavior is unchanged, including error messages, mutation ordering, and Validate-method dispatch, and Compile now also warms the plan cache. On a representative nested struct (Apple M1 Pro), Validate drops from 6350 ns and 103 allocations to 1273 ns and 17; a flat 4-field struct drops from 1010 ns and 15 allocations to 161 ns and zero. The release also exports ParseTag, which parses a dv8 tag into its directives without checking validity, enabling tooling such as an OpenAPI generator to project the directives onto other formats while keeping the tag grammar single-sourced.
Blame where blame is
This release makes dv8's errors self-attributing. Errors are now created with the microbus-io/errors package, dv8's first dependency, and carry an HTTP status code and a stack trace. A violation of the input data, such as a failed directive, a key-fold collision, or a rejection by a custom Validate method, is stamped 400 Bad Request, while a malformed directive, which is a bug in the struct tags rather than in the input, carries 500 Internal Server Error and continues to wrap dv8.ErrDirective for errors.Is matching. A custom Validate method may choose its own attribution by returning an error with any non-500 status code (e.g. errors.New("forbidden", http.StatusForbidden)), which dv8 respects; a plain error, or one carrying the default 500, is treated as a data rejection and stamped 400. Callers that route errors to HTTP responses can therefore propagate them as-is with errors.StatusCode(err) reading the code back, instead of branching on the error kind themselves. The status survives dv8's own field-path prefixing through nested structs, slices, and maps.
One `Validate` to rule them all
This release unifies dv8's API around a single name and makes the directive language explicit and strict. The dv8.ValidateContext entry point is removed: dv8.Validate now takes a context.Context as its first argument, with nil tolerated and treated as context.Background. The ValidatorContext interface is gone as well: Validator itself is now the context-taking interface, Validate(ctx context.Context) error, reaching any type in the object graph that implements it, while a parameterless Validate() error is honored as a fallback for types not written for dv8. Since Go forbids two same-named methods, the compiler enforces that a type implements at most one variant. The directive language is reworked for explicitness: directives on an array or map now apply to the container itself, the new each prefix distributes a directive to elements or map values, and the new key prefix applies directives to map keys, with mutated keys reinserted under their new value and two keys folding into one reported as an error. Consequently arrlen and maplen are retired in favor of plain len, and a nil container now has length 0, making notzero the nil check. required is renamed to notzero to say what it actually checks, main is renamed to delegate, and whitespace trimming is no longer applied by default, with notrim replaced by an opt-in trim. A comma inside a directive's value, such as a regexp quantifier, is now escaped with a backslash, written \\, in the tag, mirroring invopop/jsonschema. Directives are also strictly compiled once per type on first use: typos, misapplied directives, malformed operators or values, uncompilable regexps, and on targets that do not exist all fail with an error wrapping the new dv8.ErrDirective, which errors.Is distinguishes from invalid data, and the new dv8.Compile surfaces broken directives eagerly at startup before any data arrives. Compiled regular expressions are now cached, and a bug was fixed where a pointer-receiver context validator was silently skipped when the type did not also implement the parameterless variant. Upgrading from v0.2.x: pass a context (or nil) as the first argument of dv8.Validate, rename ValidateContext(ctx) methods to Validate(ctx), replace arrlen/maplen with len, prefix per-item directives with each, rename required to notzero and main to delegate, and add trim where trimming is desired. Note two silent behavior changes the strict compiler cannot catch: string fields that relied on implicit trimming, including required checks that counted on all-whitespace values being rejected, and container fields whose per-item rules now need an each prefix.
Copyrighter as a tool
Remove hard dependency on the copyrighter.
ValidatorContext interface
Added the option for structs to implement ValidateContext(ctx context.Context) error in addition to Validate() error
New directive "oneof"
A new directive to validate strings from a set of valid values: oneof A|B|C.
Call Validate on non-struct types as well.
Make the main Validate method variadic
v0.2.1 Variadic
Apply directives on array and map values
This release introduces a breaking change.
Directives placed on an array or map field now apply to the value elements of the array or map rather than to the array or map themselves.
New directives, "arrlen" and "maplen", were introduced to check the length of the array or map.
Correctly handle types that wrap a primitive type
Such as type MyEnum int
Better detection of Validator interface
v0.1.5 Merge branch 'main' of https://github.com/microbus-io/dv8