Documentation
¶
Overview ¶
Package verax provides configurable and extensible rules for validating data of various types.
Index ¶
- Constants
- Variables
- func AsRuleBuilder[T any](fn func(*spec.Spec) (T, error)) spec.Builder[Rule]
- func Builders() map[string]spec.Builder[Rule]
- func EnsureString(value any) (string, error)
- func Indirect(v any) any
- func IsEmpty(v any) bool
- func IsInternalError(err error) bool
- func IsNil(v any) bool
- func IsValidationError(err error) bool
- func IsVeraxError(err error) bool
- func LengthOfValue(value any) (int, error)
- func NewError(msg string, args ...any) error
- func NewErrorf(format string, args ...any) error
- func NewInternalError(msg string, args ...any) error
- func NewInternalErrorf(format string, args ...any) error
- func StringOrBytes(value any) (isString bool, str string, isBytes bool, bs []byte)
- func ToAnySlice[T any](vls ...T) []any
- func Validate(v any, rules ...Rule) error
- func ValidateNamed(name string, have any, rules ...Rule) error
- func ValidateStruct(v any, fields ...FieldRule) error
- type AbsentRule
- type ByRule
- type CompareFunc
- type ContainRule
- type EachRule
- type EqualFunc
- type EqualRule
- type Error
- type FailRule
- type FieldErrors
- type FieldRule
- type InRule
- type InternalError
- type IsFunc
- type LengthRule
- type MapKey
- type MapRule
- type MatchRule
- type Named
- type NoopRule
- type RangeRule
- func (r RangeRule) Code(code string) RangeRule
- func (r RangeRule) Exclusive() RangeRule
- func (r RangeRule) Message(tpl string) RangeRule
- func (r RangeRule) Spec() (*spec.Spec, error)
- func (r RangeRule) Validate(have any) error
- func (r RangeRule) When(condition bool) RangeRule
- func (r RangeRule) With(fn CompareFunc) RangeRule
- type RequiredRule
- type Rule
- type RuleFunc
- type Set
- type SkipRule
- type SpecableRule
- type TypeRule
- type Validator
- type WhenRule
- type WithValidator
Examples ¶
Constants ¶
const ( // ECReqNil represents error code when a value must be nil but is not. ECReqNil = "ECReqNil" // ECReqEmpty represents error code when a value must be empty but is not. ECReqEmpty = "ECReqEmpty" )
AbsentRule error codes.
const ( // ECNotEqual indicates that a value does not match the expected one. ECNotEqual = "ECNotEqual" // ECEqual indicates that a value unexpectedly matches the expected one. ECEqual = "ECEqual" )
EqualRule error codes.
const ( // MapRuleName represents [MapRule] name. MapRuleName = "map-rule" // MapKeyName represents [MapKey] name. MapKeyName = "map-key" )
Rule names.
const ( // ECMapKeyMissing represents error code for the missing required key. ECMapKeyMissing = "ECMapKeyMissing" // ECMapKeyUnexpected represents error code for the not expected key. ECMapKeyUnexpected = "ECMapKeyUnexpected" )
MapRule and MapKey error codes.
const ( // ECReq represents error code for missing required value. ECReq = "ECRequired" // ECReqNotEmpty represents error code for not nil but empty value. ECReqNotEmpty = "ECReqNotEmpty" // ECReqNotNil represents error code for nil value. ECReqNotNil = "ECReqNotNil" )
RequiredRule rule error codes.
const ( // ECInternal is the error code for internal error (library misuse). ECInternal = "ECInternal" // ECInvType is the error code for an unexpected type. ECInvType = "ECInvType" // ECInvFormat is the error code for an invalid value format. ECInvFormat = "ECInvFormat" // ECInvValue is the error code for invalid value. ECInvValue = "ECInvValue" // ECUnkRule represents an unknown rule error code. ECUnkRule = "ECUnkRule" // ECInvRuleMode is error code used when a [spec.Spec] defines unsupported // rule mode. ECInvRuleMode = "ECInvRuleMode" )
Package level error codes.
const ( ArgErrMsg = "err_msg" // Custom error message. ArgErrCode = "err_code" // Custom error code. ArgMode = "mode" // Rule mode (e.g., exclusive, inclusive). ArgMin = "min" // Minimum value. ArgMax = "max" // Maximum value. ArgOptional = "optional" // Optional value flag. ArgAllowUnk = "allow_unknown" // Allow unknown map keys. )
Spec argument names used in rule serialization.
const AbsentRuleName = "absent-rule"
AbsentRuleName represents AbsentRule name.
const ByRuleName = "by-rule"
ByRuleName represents ByRule name.
const ContainRuleName = "contain-rule"
ContainRuleName represents ContainRule name.
const ECInvIn = "ECInvIn"
ECInvIn represents error code for invalid in rule.
const ECInvLength = "ECInvLength"
ECInvLength represents error code for invalid length.
const ECInvMatch = "ECInvMatch"
ECInvMatch represents error code for an invalid regexp match.
const (
// ECInvRange is the error code for values failing range conditions.
ECInvRange = "ECInvRange"
)
RangeRule rule error codes.
const EachRuleName = "each-rule"
EachRuleName represents EachRule name.
const EqualRuleName = "equal-rule"
EqualRuleName represents EqualRule name.
const ErrorTag = "json"
ErrorTag is the default struct tag name used to customize the error field name for a struct field.
const FailRuleName = "fail-rule"
FailRuleName represents FailRule name.
const InRuleName = "in-rule"
InRuleName represents InRule name.
const LengthRuleName = "length-rule"
LengthRuleName represents LengthRule name.
const MatchRuleName = "match-rule"
MatchRuleName represents MatchRule name.
const NoopRuleName = "noop-rule"
NoopRuleName represents NoopRule name.
const RangeRuleName = "range-rule"
RangeRuleName represents RangeRule name.
const RequiredRuleName = "required-rule"
RequiredRuleName represents RequiredRule name.
const SetRuleName = "set-rule"
SetRuleName represents Set name.
const SkipRuleName = "skip-rule"
SkipRuleName represents SkipRule name.
Variables ¶
var ( // Nil conditions if a value is nil. Nil = AbsentRule{ // contains filtered or unexported fields } // Empty conditions if a not nil value is empty. Empty = AbsentRule{ // contains filtered or unexported fields } )
Rules based on AbsentRule.
var ( // ErrNotMapPtr is the error returned when the validated value is not a map. ErrNotMapPtr = NewInternalError("only a map can be validated", ECInternal) // ErrInvKeyType is the error returned when a map key type is incorrect. ErrInvKeyType = NewInternalError("the key type does not match the map", ECInternal) // ErrKeyMissing is the error returned when a required map key is missing. ErrKeyMissing = NewError("missing key", ECMapKeyMissing) // ErrKeyUnexpected is the error returned when a map key is unexpected. ErrKeyUnexpected = NewError("unexpected key", ECMapKeyUnexpected) )
MapRule sentinel errors.
var ( // Required is a validation rule that conditions if a value is not empty. // // A value is considered not empty if: // - integer, float: not zero // - bool: always not empty // - string, array, slice, map: len() > 0 // - interface, pointer: not nil, and the referenced value is not empty // - other: IsZero() returns false Required = RequiredRule{ // contains filtered or unexported fields } // NotEmpty conditions if a value is either nil or not empty. It differs // from [Required] in that it treats a nil pointer or nil interface as valid. NotEmpty = RequiredRule{ // contains filtered or unexported fields } // NotNil is a validation rule that conditions if a value is not nil. It // applies to interface, pointer, slice, and map types. All other types are // considered valid. NotNil = RequiredRule{ // contains filtered or unexported fields } )
Rules based on RequiredRule.
var ( // ErrInvType is returned in case of an unexpected value type. ErrInvType = NewInternalError("unexpected value type", ECInvType) )
Package level sentinel errors.
var ( // ErrNotStructPtr is an error returned from [ValidateStruct] when a // non-pointer struct is used. ErrNotStructPtr = NewInternalError("requires a struct pointer", ECInternal) )
Struct validation errors.
var Noop = NoopRule{}
Noop is an always passing no-op rule.
var Skip = SkipRule(true)
Skip is a special validation rule that indicates all rules following it should be skipped.
Functions ¶
func AsRuleBuilder ¶ added in v0.3.0
AsRuleBuilder wraps a typed spec constructor and returns a [Builder] for it.
func Builders ¶ added in v0.3.0
Builders returns a map of builder definitions for all built-in rule specs. The returned map is a new instance and can be safely modified by callers.
Example (By) ¶
package main
import (
"fmt"
"github.com/ctx42/verax/pkg/spec"
"github.com/ctx42/verax/pkg/verax"
)
// nonEmptyWord is a custom RuleFunc used in ExampleBuilders_by.
// Declared as verax.RuleFunc so the type assertion in ByRuleFromSpec succeeds.
var nonEmptyWord verax.RuleFunc = func(v any) error {
str, err := verax.EnsureString(v)
if err != nil {
return verax.ErrInvType
}
if len(str) < 3 {
return verax.NewError("must be at least 3 characters long", "ECShort")
}
return nil
}
func main() {
// Register the function as a named source so it survives serialization.
src, _ := spec.NewSource("nonEmptyWord", nonEmptyWord)
reg := spec.NewRegistry[verax.Rule]()
reg.RegisterBuilders(verax.Builders())
reg.RegisterSource(src)
// Encode.
rule := verax.By(nonEmptyWord)
spc, _ := rule.Spec()
data, _ := reg.EncodeSpec(spc)
// Decode and rebuild — the function is resolved by name from the registry.
restored, _ := reg.DecodeAndBuild(data)
err := verax.Validate("hi", restored)
fmt.Println(err)
}
Output: must be at least 3 characters long
Example (Decode) ¶
package main
import (
"fmt"
"github.com/ctx42/verax/pkg/spec"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
data := []byte(`{"name":"range-rule","args":{"mode":{"type":"string","value":"min"},"value":{"type":"int","value":18}}}`)
reg := spec.NewRegistry[verax.Rule]()
reg.RegisterBuilders(verax.Builders())
restored, _ := reg.DecodeAndBuild(data)
err := verax.Validate(15, restored)
fmt.Println(err)
}
Output: must be greater or equal to 18
Example (Encode) ¶
package main
import (
"fmt"
"github.com/ctx42/verax/pkg/spec"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
rule := verax.Min(18)
reg := spec.NewRegistry[verax.Rule]()
reg.RegisterBuilders(verax.Builders())
spc, _ := rule.Spec()
data, _ := reg.EncodeSpec(spc)
fmt.Println(string(data))
}
Output: {"name":"range-rule","args":{"mode":"min","value":{"type":"int","value":18}}}
func EnsureString ¶
EnsureString ensures the given value is a string. If the value is a byte slice, it will be typecast into a string. An error is returned otherwise.
func Indirect ¶
Indirect dereferences the given value if it is a pointer or interface, returning the underlying value. If the value implements driver.Valuer, it returns the result of calling its Value method. If the input is nil or not a pointer/interface, it is returned unchanged.
Examples:
- For a *int pointing to 42, it returns 42.
- For an interface{} containing a *string, it returns the string.
- For a driver.Valuer, it returns the result of Value().
func IsEmpty ¶
IsEmpty checks if a value is empty.
A value is considered empty if:
- integer, float: zero
- bool: never empty
- string, array, slice, map, chan: nil or len() == 0
- interface, pointer: nil or the referenced value is empty
- other: IsZero() == true
If the input is nil, it returns true.
func IsInternalError ¶ added in v0.3.0
IsInternalError reports whether the error is non-nil InternalError.
func IsNil ¶
IsNil checks whether the provided value is actual nil or wrapped nil. Actual nil means the interface itself has no type or value (have == nil).
func IsValidationError ¶ added in v0.3.0
IsValidationError reports whether the error is non-nil Error or FieldErrors.
func IsVeraxError ¶ added in v0.5.0
IsVeraxError reports whether the error is non-nil Error, FieldErrors or InternalError.
func LengthOfValue ¶
LengthOfValue returns the length of a value that is a string, slice, map, or array. An error is returned for all other types.
func NewError ¶ added in v0.3.0
NewError creates a new Error with the given message. The args may contain an optional string error code and any number of xrr.Option values in any order.
Examples:
NewError("message")
NewError("message", "ECode")
NewError("message", "ECode", xrr.Option())
When xrr.WithCause is provided:
- If msg is empty, Error() returns the cause's message directly.
- If msg is non-empty, Error() returns "msg: cause message".
- If no code is given and xrr.WithCode is not provided, the cause's code is inherited via xrr.GetCode. Pass a code string or xrr.WithCode to override it.
For wrapping without a new message, prefer xrr.Wrap, which makes the intent clearer.
func NewErrorf ¶ added in v0.7.0
NewErrorf creates a new Error using a format string. It is the format-style counterpart of NewError: non-xrr.Option args are passed to the format string, while xrr.Option values are applied to the error. Unlike NewError, a bare string argument is treated as a format argument, not an error code — pass xrr.WithCode to set the code.
When the format string contains %w, the error is created via fmt.Errorf and stored as the cause; xrr.GenericError.Error delegates to it. Without %w, the message is set to fmt.Sprintf(format, args...).
Examples:
NewErrorf("user %d not found", userID)
NewErrorf("user %d not found", userID, xrr.WithCode("ECode"))
NewErrorf("connect failed: %w", err)
NewErrorf("connect failed: %w", err, xrr.WithCode("ECode"))
func NewInternalError ¶ added in v0.3.0
NewInternalError creates a new InternalError with the given message. The args may contain an optional string error code and any number of xrr.Option values in any order.
Examples:
NewInternalError("message")
NewInternalError("message", "ECode")
NewInternalError("message", "ECode", xrr.Option())
When xrr.WithCause is provided:
- If msg is empty, Error() returns the cause's message directly.
- If msg is non-empty, Error() returns "msg: cause message".
- If no code is given and xrr.WithCode is not provided, the cause's code is inherited via xrr.GetCode. Pass a code string or xrr.WithCode to override it.
For wrapping without a new message, prefer xrr.Wrap, which makes the intent clearer.
func NewInternalErrorf ¶ added in v0.7.0
NewInternalErrorf creates a new InternalError using a format string. It is the format-style counterpart of NewInternalError: non-xrr.Option args are passed to the format string, while xrr.Option values are applied to the error. Unlike NewInternalError, a bare string argument is treated as a format argument, not an error code — pass xrr.WithCode to set the code.
When the format string contains %w, the error is created via fmt.Errorf and stored as the cause; xrr.GenericError.Error delegates to it. Without %w, the message is set to fmt.Sprintf(format, args...).
Examples:
NewInternalErrorf("user %d not found", userID)
NewInternalErrorf("user %d not found", userID, xrr.WithCode("ECode"))
NewInternalErrorf("connect failed: %w", err)
NewInternalErrorf("connect failed: %w", err, xrr.WithCode("ECode"))
func StringOrBytes ¶
StringOrBytes typecasts a value into a string or byte slice. Boolean flags are returned to indicate if the typecasting succeeds or not.
func ToAnySlice ¶
ToAnySlice returns a slice of T as a slice of any.
func Validate ¶
Validate checks the given value against the provided validation rules. Returns nil if all rules pass, or the first validation error encountered. Skips validation if one of the rules is Skip. Supports types implementing Validator or WithValidator, and recursively validates maps, slices, arrays, pointers, or interfaces with validatable elements. Returns nil for nil pointers or interfaces.
nolint: cyclop
Example (Maps) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Planet struct {
Position int `json:"position"`
Name string `json:"name" solar:"planet_name"`
Life float64
}
func (p *Planet) Validate() error {
return verax.ValidateStruct(
p,
verax.Field(&p.Position, verax.Min(1), verax.Max(8)),
verax.Field(&p.Name, verax.Length(4, 7)).Tag("solar"),
verax.Field(&p.Life, verax.Min(0.0), verax.Max(1.0)),
)
}
func main() {
planets := map[string]*Planet{
"mer": {1, "Mer", 0},
"ear": {3, "Earth", 1.0},
"x": {9, "X", 0.1},
}
err := verax.Validate(planets)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - mer.planet_name: the length must be between 4 and 7 - x.planet_name: the length must be between 4 and 7 - x.position: must be less or equal to 8 JSON: { "mer.planet_name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" }, "x.planet_name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" }, "x.position": { "code": "ECInvRange", "error": "must be less or equal to 8" } }
Example (Primitive_int) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
err := verax.Validate(
45,
verax.Required,
verax.Min(42),
verax.Max(44),
)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - must be less or equal to 44 JSON: { "code": "ECInvRange", "error": "must be less or equal to 44" }
Example (Slices) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Planet struct {
Position int `json:"position"`
Name string `json:"name" solar:"planet_name"`
Life float64
}
func (p *Planet) Validate() error {
return verax.ValidateStruct(
p,
verax.Field(&p.Position, verax.Min(1), verax.Max(8)),
verax.Field(&p.Name, verax.Length(4, 7)).Tag("solar"),
verax.Field(&p.Life, verax.Min(0.0), verax.Max(1.0)),
)
}
func main() {
planets := []*Planet{
{1, "Mer", 0},
{3, "Earth", 1.0},
{9, "X", 0.1},
}
err := verax.Validate(planets)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - 0.planet_name: the length must be between 4 and 7 - 2.planet_name: the length must be between 4 and 7 - 2.position: must be less or equal to 8 JSON: { "0.planet_name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" }, "2.planet_name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" }, "2.position": { "code": "ECInvRange", "error": "must be less or equal to 8" } }
func ValidateNamed ¶
ValidateNamed validates v using the provided rules, wrapping any error in FieldErrors with the specified field name.
func ValidateStruct ¶
ValidateStruct validates a struct by checking the specified struct fields against the corresponding validation rules. Note that the struct being validated must be specified as a pointer to it. If the pointer is nil, it is considered valid. Use Field() to specify struct fields that need to be validated. Each Field() call specifies a single field which should be specified as a pointer to the field. A field can be associated with multiple rules.
For example,
value := struct {
Name string
Value string
}{"name", "demo"}
err := validation.ValidateStruct(&value,
verax.Field(&a.Name, verax.Required),
verax.Field(&a.Value, verax.Required, verax.Length(5, 10)),
)
fmt.Println(err)
// Value: the length must be between 5 and 10.
Returns InternalError on unexpected errors, otherwise it returns FieldErrors error.
nolint: cyclop
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Planet struct {
Position int `json:"position"`
Name string `json:"name" solar:"planet_name"`
Life float64
}
func (p *Planet) Validate() error {
return verax.ValidateStruct(
p,
verax.Field(&p.Position, verax.Min(1), verax.Max(8)),
verax.Field(&p.Name, verax.Length(4, 7)).Tag("solar"),
verax.Field(&p.Life, verax.Min(0.0), verax.Max(1.0)),
)
}
func main() {
planet := Planet{9, "PlanetXYZ", -1}
err := verax.ValidateStruct(
&planet,
verax.Field(&planet.Position, verax.Min(1), verax.Max(8)),
verax.Field(&planet.Name, verax.Length(4, 7)),
verax.Field(&planet.Life, verax.Min(0.0), verax.Max(1.0)),
)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - Life: must be greater or equal to 0 - name: the length must be between 4 and 7 - position: must be less or equal to 8 JSON: { "Life": { "code": "ECInvRange", "error": "must be greater or equal to 0" }, "name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" }, "position": { "code": "ECInvRange", "error": "must be less or equal to 8" } }
Example (Custom_tag) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Planet struct {
Position int `json:"position"`
Name string `json:"name" solar:"planet_name"`
Life float64
}
func (p *Planet) Validate() error {
return verax.ValidateStruct(
p,
verax.Field(&p.Position, verax.Min(1), verax.Max(8)),
verax.Field(&p.Name, verax.Length(4, 7)).Tag("solar"),
verax.Field(&p.Life, verax.Min(0.0), verax.Max(1.0)),
)
}
func main() {
planet := Planet{1, "Mer", 0.0}
err := verax.ValidateStruct(
&planet,
verax.Field(&planet.Name, verax.Length(4, 7)).Tag("solar"),
)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - planet_name: the length must be between 4 and 7 JSON: { "planet_name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" } }
Types ¶
type AbsentRule ¶ added in v0.3.0
type AbsentRule struct {
// contains filtered or unexported fields
}
AbsentRule conditions if a value is absent.
func AbsentRuleFromSpec ¶ added in v0.3.0
func AbsentRuleFromSpec(spc *spec.Spec) (AbsentRule, error)
AbsentRuleFromSpec creates an instance of AbsentRule from the spec.Spec.
func (AbsentRule) Code ¶ added in v0.3.0
func (r AbsentRule) Code(code string) AbsentRule
func (AbsentRule) Message ¶ added in v0.3.0
func (r AbsentRule) Message(msg string) AbsentRule
func (AbsentRule) Validate ¶ added in v0.3.0
func (r AbsentRule) Validate(have any) error
func (AbsentRule) When ¶ added in v0.3.0
func (r AbsentRule) When(condition bool) AbsentRule
type ByRule ¶
type ByRule struct {
// contains filtered or unexported fields
}
ByRule conditions if a value passed to a RuleFunc function is valid.
func By ¶
By validates a value using the provided RuleFunc. This is the primary escape hatch for custom validation logic. The function receives the value and should return nil on success or a verax domain error on failure (see Rule). Use Check for the simple func(bool) case.
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
fn := func(v any) error {
str, err := verax.EnsureString(v)
if err != nil {
return verax.ErrInvType
}
if str != "" && str != "abc" {
return verax.NewError("i need abc", "ECMustABC")
}
return nil
}
AbcRule := verax.By(fn)
err := AbcRule.Validate("xyz") // nolint: ineffassign
// or
err = verax.Validate("xyz", AbcRule)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - i need abc JSON: { "code": "ECMustABC", "error": "i need abc" }
func ByRuleFromSpec ¶ added in v0.3.0
ByRuleFromSpec creates a new instance of ByRule from the spec.Spec.
type CompareFunc ¶
CompareFunc is a custom validation function that compares two values and returns the result of the comparison. The integer result is the same as in the cmp.Compare function.
-1 if "want" is less than "have", 0 if "want" equals "have", +1 if "want" is greater than "have".
The error result must be an InternalError for misuse such as unsupported or mismatched types, and should be an Error for comparison failures.
type ContainRule ¶
type ContainRule struct {
// contains filtered or unexported fields
}
ContainRule is a validation rule that validates there is at least one element in a map/slice/array using the specified EqualRule.
func Contain ¶
func Contain(rule EqualRule) ContainRule
Contain returns a validation rule that loops through iterable (map, slice, or array) and validates it contains at least one given value.
func ContainRuleFromSpec ¶ added in v0.3.0
func ContainRuleFromSpec(spc *spec.Spec) (ContainRule, error)
ContainRuleFromSpec creates a new instance of ContainRule from the spec.Spec.
func (ContainRule) Code ¶ added in v0.3.0
func (r ContainRule) Code(code string) ContainRule
func (ContainRule) Message ¶ added in v0.3.0
func (r ContainRule) Message(msg string) ContainRule
func (ContainRule) Validate ¶
func (r ContainRule) Validate(have any) error
func (ContainRule) When ¶ added in v0.3.0
func (r ContainRule) When(condition bool) ContainRule
type EachRule ¶
type EachRule struct {
// contains filtered or unexported fields
}
EachRule is a validation rule that validates elements in a map/slice/array using the specified list of rules.
func Each ¶
Each returns a validation rule that loops through an iterable (map, slice, or array) and validates each value inside with the provided rules. Empty iterable is considered valid. Use the Required rule to make sure the iterable is not empty.
func EachRuleFromSpec ¶ added in v0.3.0
EachRuleFromSpec creates a new instance of EachRule from the spec.Spec.
type EqualFunc ¶ added in v0.3.0
EqualFunc is the signature for a custom comparison function. It receives the expected value (want) and the value under validation (have) and returns nil when the comparison criterion is satisfied. When the criterion is violated, it should return an Error; which condition counts as a violation depends on the context the function is used in. For example, in an equality context the function returns an error when "want" and "have" differ, whereas in an inequality context it returns an error when they are equal. It must return an InternalError for misuse such as unsupported or mismatched types.
type EqualRule ¶
type EqualRule struct {
// contains filtered or unexported fields
}
EqualRule is a rule that conditions a value matches the expected value using the provided comparison function.
func EqualField ¶
EqualField constructs rule conditioning a validated value is equal to "want". When it isn't, the error message will say the value must be equal to "field".
func EqualRuleFromSpec ¶ added in v0.3.0
EqualRuleFromSpec creates a new instance of EqualRule from the spec.Spec.
func NotEqualField ¶
NotEqualField constructs rule conditioning a validated value is not equal to "want". When it is the error message will say the value must not be equal to "field".
type Error ¶
type Error = xrr.GenericError[edError]
Error represents an error in the package's error domain.
type FailRule ¶ added in v0.3.0
type FailRule struct {
// contains filtered or unexported fields
}
FailRule returns an error when its condition evaluates to true.
By default, the condition is always true (fails unconditionally). Use FailRule.When to specify a custom condition.
func Fail ¶ added in v0.3.0
Fail returns the rule which fails with a given error when the condition is true. By default, the condition is always true.
func FailRuleFromSpec ¶ added in v0.3.0
FailRuleFromSpec creates a new instance of FailRule from the spec.Spec.
type FieldErrors ¶ added in v0.5.0
type FieldErrors = xrr.GenericFields[edError]
FieldErrors represents a field error in the package's error domain.
func NewFieldError ¶ added in v0.5.0
func NewFieldError(field string, err error) *FieldErrors
NewFieldError returns a new field error in the package's error domain.
func NewFieldErrors ¶ added in v0.5.0
func NewFieldErrors(fields map[string]error) *FieldErrors
NewFieldErrors creates a new FieldErrors from the given map. The map is stored directly without copying.
type FieldRule ¶ added in v0.3.0
type FieldRule struct {
// contains filtered or unexported fields
}
FieldRule represents a rule set associated with a struct field.
type InRule ¶
type InRule struct {
// contains filtered or unexported fields
}
InRule is a validation rule that validates if a value can be found in the given list of values.
func In ¶
In returns a validation rule that conditions if a value can be found in the given list of values. Note that the value being conditioned and the possible range of values must be of the same type. Values are compared using reflect.DeepEqual. An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func InRuleFromSpec ¶ added in v0.3.0
InRuleFromSpec creates a new instance of InRule from the spec.Spec.
func NotIn ¶
NotIn returns a validation rule that conditions if a value cannot be found in the given list of values. Note that the value being conditioned and the possible range of values must be of the same type. Values are compared using reflect.DeepEqual. An empty value is considered valid. Use the Required rule to make sure a value is not empty.
type InternalError ¶ added in v0.3.0
type InternalError = xrr.GenericError[edInternal]
InternalError represents an internal error (library misuse) in the package's error domain.
type IsFunc ¶ added in v0.3.0
IsFunc represents a validator function returning true only for valid values. You may wrap it as a Rule by calling Check.
type LengthRule ¶
type LengthRule struct {
// contains filtered or unexported fields
}
LengthRule is a validation rule that conditions if a value's length is within the specified range.
func Length ¶
func Length(minimum, maximum int) LengthRule
Length returns a validation rule that conditions if a value's length is within the specified range. If max is 0, it means there is no upper bound for the length. This rule should only be used for validating strings, slices, maps, and arrays. An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func LengthRuleFromSpec ¶ added in v0.3.0
func LengthRuleFromSpec(spc *spec.Spec) (LengthRule, error)
LengthRuleFromSpec creates a new instance of LengthRule from the spec.Spec.
func RuneLength ¶
func RuneLength(minimum, maximum int) LengthRule
RuneLength returns a validation rule that conditions if a string's rune length is within the specified range. If max is 0, it means there is no upper bound for the length. This rule should only be used for validating strings, slices, maps, and arrays. An empty value is considered valid. Use the Required rule to make sure a value is not empty. If the value being validated is not a string, the rule works the same as Length.
func (LengthRule) Code ¶
func (r LengthRule) Code(code string) LengthRule
func (LengthRule) Message ¶ added in v0.3.0
func (r LengthRule) Message(tpl string) LengthRule
func (LengthRule) When ¶
func (r LengthRule) When(condition bool) LengthRule
type MapKey ¶ added in v0.3.0
type MapKey struct {
// contains filtered or unexported fields
}
MapKey represents a rule set associated with a map key.
func Key ¶
Key specifies a map key and the corresponding validation rules. Use with Map to validate specific entries (supports [Optional], When, chaining, and [Specable] roundtrips).
func MapKeyFromSpec ¶ added in v0.3.0
MapKeyFromSpec creates a new instance of MapKey from the spec.Spec.
type MapRule ¶
type MapRule struct {
// contains filtered or unexported fields
}
MapRule represents a rule set associated with map types.
func Map ¶
Map returns a validation rule that conditions the keys and values of a map. This rule should only be used for validating maps, or a validation error will be reported. Use Key() to specify map keys that need to be validated. Each Key() call specifies a single key which can be associated with multiple rules.
For example,
verax.Map(
verax.Key("Name", verax.Required),
verax.Key("Value", verax.Required, verax.Length(5, 10)),
)
A nil value is considered valid. Use the Required rule to make sure a map value is present.
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"time"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
data := map[string]any{
"bool": false,
"int": 44,
"float": 0.1,
"time": time.Date(2000, 1, 2, 3, 4, 5, 0, time.UTC),
}
MyRule := verax.Map(
verax.Key("bool", verax.Equal(true)),
verax.Key("int", verax.Max(42)),
verax.Key("float", verax.Min(4.2)),
verax.Key("time", verax.Min(
time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC),
)),
)
err := verax.Validate(data, MyRule) // nolint: ineffassign
// or
err = MyRule.Validate(data)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - bool: must be equal to 'true' - float: must be greater or equal to 4.2 - int: must be less or equal to 42 - time: must be greater or equal to 2025-01-01T00:00:00Z JSON: { "bool": { "code": "ECNotEqual", "error": "must be equal to 'true'" }, "float": { "code": "ECInvRange", "error": "must be greater or equal to 4.2" }, "int": { "code": "ECInvRange", "error": "must be less or equal to 42" }, "time": { "code": "ECInvRange", "error": "must be greater or equal to 2025-01-01T00:00:00Z" } }
func MapRuleFromSpec ¶ added in v0.3.0
MapRuleFromSpec creates a new instance of MapRule from the spec.Spec.
func (MapRule) AllowUnknown ¶
AllowUnknown configures the rule to ignore unknown keys.
func (MapRule) IsOptional ¶
IsOptional returns true if the given map key is optional. It will return true for keys that are not defined in the map.
type MatchRule ¶
type MatchRule struct {
// contains filtered or unexported fields
}
MatchRule is a validation rule that conditions if a value matches the specified regular expression.
func Match ¶
Match returns a validation rule that conditions if a value matches the specified regular expression. This rule should only be used for validating strings and byte slices, or a validation error will be reported. An empty value is considered valid. Use the Required rule to make sure a value is not empty.
func MatchRuleFromSpec ¶ added in v0.3.0
MatchRuleFromSpec creates a new instance of MatchRule from the spec.Spec.
type Named ¶
Named represents a collection of named rules.
func (Named) GetOrError ¶
GetOrError returns the named rule; when it doesn't exist, it returns an instance of Fail with ECUnkRule error code.
type NoopRule ¶ added in v0.3.0
type NoopRule struct{}
NoopRule is a special validation rule that always passes.
func NoopRuleFromSpec ¶ added in v0.3.0
NoopRuleFromSpec creates a new instance of NoopRule from the spec.Spec.
type RangeRule ¶ added in v0.3.0
type RangeRule struct {
// contains filtered or unexported fields
}
RangeRule is a rule validating a value satisfies a given range.
func Max ¶
Max creates a validation rule that conditions if a value is less than or equal to the specified range. Use RangeRule.Exclusive to enforce a strict less-than condition. The value being conditioned and the range must be of the same type, supporting only int, uint, float, and time.Time types. Empty values are considered valid; use the Required rule to ensure a value is not empty.
Example:
rule := Max(100) // Value must be <= 100 rule := Max(100).Exclusive() // Value must be < 100
func Min ¶
Min creates a validation rule that conditions if a value is greater than or equal to the specified range. Use RangeRule.Exclusive to enforce a strict greater-than condition. The value being conditioned and the range must be of the same type, supporting only int, uint, float, and time.Time types. Empty values are considered valid; use the Required rule to ensure a value is not empty.
Example:
rule := Min(10) // Value must be >= 10 rule := Min(10).Exclusive() // Value must be > 10
func RangeRuleFromSpec ¶ added in v0.3.0
RangeRuleFromSpec creates a new instance of RangeRule from the spec.Spec.
func (RangeRule) Exclusive ¶ added in v0.3.0
Exclusive modifies a RangeRule to exclude the boundary value, enforcing a strict comparison. For example, when used with Min, it conditions if a value is strictly greater than the range, and with Max, it conditions if a value is strictly less than the range. The value and range must be of the same type.
It must be called before RangeRule.Code or RangeRule.Message.
Example:
ruleMin := Min(10).Exclusive() // Value must be > 10 ruleMax := Max(100).Exclusive() // Value must be < 100
func (RangeRule) With ¶ added in v0.3.0
func (r RangeRule) With(fn CompareFunc) RangeRule
With sets a custom comparison function for a RangeRule, overriding the default comparison behavior. The function, of type CompareFunc, defines how the value is compared to the range. This is useful for custom validation logic.
The function must return an error if the type is not supported.
Example:
cmpMyType := func(a, b any) int { ... }
rule := Min(myTypeValue).With(cmpMyType)
type RequiredRule ¶ added in v0.3.0
type RequiredRule struct {
// contains filtered or unexported fields
}
RequiredRule is a rule that conditions if a value is not empty.
func RequiredRuleFromSpec ¶ added in v0.3.0
func RequiredRuleFromSpec(spc *spec.Spec) (RequiredRule, error)
RequiredRuleFromSpec creates a new instance of RequiredRule from the spec.Spec.
func (RequiredRule) Code ¶ added in v0.3.0
func (r RequiredRule) Code(code string) RequiredRule
func (RequiredRule) Message ¶ added in v0.3.0
func (r RequiredRule) Message(msg string) RequiredRule
func (RequiredRule) Validate ¶ added in v0.3.0
func (r RequiredRule) Validate(have any) error
func (RequiredRule) When ¶ added in v0.3.0
func (r RequiredRule) When(condition bool) RequiredRule
type Rule ¶
type Rule interface {
// Validate checks the value against the rule's condition(s) and returns
// a validation error if it fails.
//
// Implementations should return [Error] instance (or nil on success) to
// support error targeting with [errors.As] and [errors.Is].
//
// For rules that delegate to custom validation functions ([RuleFunc],
// [EqualFunc], [CompareFunc], etc.):
//
// - If both message and code are overridden: return a new [Error] instance.
// - If only the message is overridden: return a new [Error] instance with
// the original error's code and the custom message.
// - If only the code is overridden: return the original error after
// calling [SetCode] on it.
// - If neither is overridden: return the error from the validation
// function unchanged.
//
// Callers implementing custom validation via [RuleFunc], [EqualFunc],
// [CompareFunc], [Validator], etc. are responsible for returning a verax
// domain error (or nil). The library does not wrap foreign errors for now.
Validate(have any) error
}
Rule represents a validation rule.
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/xrr/pkg/xrr"
"github.com/ctx42/verax/pkg/verax"
)
type UserDoesNotExistRule struct{}
func (u UserDoesNotExistRule) Validate(v any) error {
username, err := verax.EnsureString(v)
if err != nil {
return verax.ErrInvType
}
format := "user %s already exist"
return verax.NewErrorf(format, username, xrr.WithCode("ECMustNotExist"))
}
func main() {
err := verax.Validate("thor", verax.Required, UserDoesNotExistRule{})
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - user thor already exist JSON: { "code": "ECMustNotExist", "error": "user thor already exist" }
Example (Conditioned) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Range struct {
Start int
End int
}
func main() {
r := Range{Start: 51, End: 42}
err := verax.ValidateStruct(
&r,
verax.Field(&r.End, verax.Min(100).When(r.Start > 50)),
)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - End: must be greater or equal to 100 JSON: { "End": { "code": "ECInvRange", "error": "must be greater or equal to 100" } }
Example (CustomErrorCode) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
rule := verax.Equal(42).Code("EC42")
err := verax.Validate(44, rule)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - must be equal to '42' JSON: { "code": "EC42", "error": "must be equal to '42'" }
Example (CustomMessage) ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
rule := verax.Equal(42).Message("must be my favorite number").Code("EC42")
err := verax.Validate(44, rule)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - must be my favorite number JSON: { "code": "EC42", "error": "must be my favorite number" }
type RuleFunc ¶
RuleFunc represents a custom validation function.
It receives a value and must return nil on success, an Error for validation failures, or an InternalError for misuse such as unsupported or mismatched types.
type Set ¶
type Set []Rule
Set groups multiple validation rules and implements the Rule interface.
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
func main() {
NameRule := verax.Set{
verax.Required,
verax.Length(4, 5),
}
err := NameRule.Validate("abc") // nolint: ineffassign
// or
err = verax.Validate("abc", NameRule)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - the length must be between 4 and 5 JSON: { "code": "ECInvLength", "error": "the length must be between 4 and 5" }
func SetRuleFromSpec ¶ added in v0.6.0
SetRuleFromSpec creates a new instance of Set from the spec.Spec.
type SkipRule ¶ added in v0.3.0
type SkipRule bool
SkipRule represents a validation rule that skips later rules.
func SkipRuleFromSpec ¶ added in v0.3.0
SkipRuleFromSpec creates a new instance of SkipRule from the spec.Spec.
type SpecableRule ¶ added in v0.3.0
SpecableRule is implemented by rules that can both validate values and describe themselves as a spec.Spec. Implement this interface to make a rule serializable, for example, when storing validation configuration in a database or exchanging it over an API.
type TypeRule ¶ added in v0.2.0
type TypeRule struct {
// contains filtered or unexported fields
}
TypeRule is a rule validating a value is of the expected type.
func Type ¶ added in v0.2.0
Type is a rule validating a value is of the given reflect.Type.
type Validator ¶
type Validator interface {
// Validate validates the implementer and returns an error on failure.
Validate() error
}
Validator wraps Validate method.
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Planet struct {
Position int `json:"position"`
Name string `json:"name" solar:"planet_name"`
Life float64
}
func (p *Planet) Validate() error {
return verax.ValidateStruct(
p,
verax.Field(&p.Position, verax.Min(1), verax.Max(8)),
verax.Field(&p.Name, verax.Length(4, 7)).Tag("solar"),
verax.Field(&p.Life, verax.Min(0.0), verax.Max(1.0)),
)
}
func main() {
planet := &Planet{9, "Mer", 0.0}
err := planet.Validate()
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - planet_name: the length must be between 4 and 7 - position: must be less or equal to 8 JSON: { "planet_name": { "code": "ECInvLength", "error": "the length must be between 4 and 7" }, "position": { "code": "ECInvRange", "error": "must be less or equal to 8" } }
Example (Quick_start) ¶
package main
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
var emailRx = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
type CreateUserRequest struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func (r *CreateUserRequest) Validate() error {
return verax.ValidateStruct(r,
verax.Field(&r.Name, verax.Required, verax.Length(2, 50)),
verax.Field(&r.Email, verax.Required, verax.Match(emailRx)),
verax.Field(&r.Age, verax.Required, verax.Min(18), verax.Max(120)),
)
}
func main() {
req := &CreateUserRequest{
Name: "A",
Email: "bad-email",
Age: 15,
}
err := req.Validate()
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - age: must be greater or equal to 18 - email: must match a valid format - name: the length must be between 2 and 50 JSON: { "age": { "code": "ECInvRange", "error": "must be greater or equal to 18" }, "email": { "code": "ECInvMatch", "error": "must match a valid format" }, "name": { "code": "ECInvLength", "error": "the length must be between 2 and 50" } }
type WhenRule ¶
type WhenRule struct {
// contains filtered or unexported fields
}
WhenRule is a validation rule that applies rules from When if the condition is met, or rules from WhenRule.Else otherwise.
func When ¶
When returns a validation rule that executes the given list of rules when the condition is true. Use WhenRule.Else to specify rules for the false case. Empty values are not short-circuited (use Skip or When with IsEmpty for that).
Example ¶
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/ctx42/verax/pkg/verax"
)
type Range struct {
Start int
End int
}
func main() {
r := Range{Start: 44, End: 42}
failRule := verax.Fail("the end must be greater than the start", "ECRange")
err := verax.ValidateStruct(
&r,
verax.Field(&r.End, verax.When(r.End < r.Start, failRule)),
)
PrintError(err)
PrintJSON(err)
}
// PrintJSON marshals value to JSON string.
func PrintJSON(v any) {
data, err := json.MarshalIndent(v, "", " ")
if err != nil {
panic(err)
}
fmt.Printf("JSON:\n%s\n", string(data))
}
// PrintError formats error message.
func PrintError(err error) {
var msg strings.Builder
for line := range strings.SplitSeq(err.Error(), "; ") {
msg.WriteString("- " + line + "\n")
}
fmt.Printf("ERROR:\n\n%s\n", msg.String())
}
Output: ERROR: - End: the end must be greater than the start JSON: { "End": { "code": "ECRange", "error": "the end must be greater than the start" } }
type WithValidator ¶
WithValidator wraps ValidateWith method which validates implementor using the given Rule.