Documentation
¶
Overview ¶
Package schema provides declarative normalization, defaulting, and validation of Go values.
Types describe their own rules by implementing Schematic. Callers then run those rules with Enforce.
Quick start ¶
type Email string
func (Email) Schema() schema.Schema {
return schema.String{
TrimSpace: true,
ToLower: true,
MustBeEmail: true,
}
}
type User struct {
Email Email
Name string
}
func (User) Schema() schema.Schema {
return schema.Object{
"Email": schema.String{MustNotBeZero: true},
"Name": schema.String{MustNotBeZero: true, TrimSpace: true, MinLen: 1},
}
}
Callers run the schema by calling Enforce (or EnforceAny).
Mutation contract ¶
Enforce uses a mutate-when-possible strategy. When the caller passes a pointer, the pointee is mutated in place by normalizations and defaults; Result.Value holds the same pointer. When the caller passes a value, Go copies it on the function call, so the caller's original variable is unchanged and Result.Value holds the transformed copy. In both cases, Result.Value is the canonical way to read the output.
Two locations cannot be mutated in place even through a pointer input, because Go forbids it: map keys, and values stored by-value inside an interface field. For map keys, the containing map is rebuilt so the normalized key exists; for interface-boxed by-value fields, the new value is boxed back into the slot. Both are transparent to the caller.
Presence and zero vocabulary ¶
Two separate concepts drive rule behavior: nil-ness and zero-ness. Nil-ness is meaningful only for nilable slots (pointers, interfaces, slices, maps). Zero-ness is meaningful for scalar values and is defined per type: the empty string, numeric zero, and so on. Bool does not use zero vocabulary; it uses MustBeTrue and MustBeFalse instead.
The four control flags, applied per type where meaningful, are:
- MustNotBeNil: reject a nil slot.
- MustNotBeZero: reject a zero value after nil handling.
- DefaultIfNil: replace a nil slot with a supplied default.
- DefaultIfZero: replace a zero value after nil handling with a supplied default.
Nil handling runs as a pre-step before leaf rules. On a nil slot: DefaultIfNil (if set) materializes the value and the default flows into the leaf rule; otherwise MustNotBeNil (if set) produces a validation error and leaf processing stops. Every leaf rule therefore operates on a concrete, non-nil value.
Execution order ¶
For each leaf value: (1) nil handling and type dispatch, (2) SkipFunc on the concrete value, (3) normalize (TrimSpace, case, TransformFunc), (4) DefaultIfZero if still zero, (5) write back, (6) value validators, (7) ValidateFunc.
For objects: (1) field rules, (2) TransformFunc, (3) ValidateFunc.
Index ¶
Constants ¶
const ( TransformFunc = "\x00transform_func" ValidateFunc = "\x00validate_func" )
Reserved Object keys. These byte sequences begin with a NUL so they cannot collide with Go identifiers or typical map keys.
Variables ¶
This section is empty.
Functions ¶
func IsSchemaError ¶
IsSchemaError reports whether err is or wraps a SchemaError.
func IsValidationError ¶
IsValidationError reports whether err is or wraps a ValidationError.
Types ¶
type Any ¶
type Any struct {
MustNotBeNil bool
DefaultIfNil any
ValidateFunc func(any) error
SkipFunc func(any) bool
}
Any is an escape-hatch rule block for values that do not fit the typed blocks above. It supports nil checks, nil defaults, SkipFunc, and ValidateFunc.
type Bool ¶
type Bool struct {
MustNotBeNil bool
DefaultIfNil any
MustBeTrue bool
MustBeFalse bool
ValidateFunc func(bool) error
SkipFunc func(bool) bool
}
Bool describes rules for a bool-kinded value or field. Bool does not use zero vocabulary; use MustBeTrue or MustBeFalse for value constraints.
type Float ¶
type Float struct {
MustNotBeNil bool
MustNotBeZero bool
DefaultIfNil any
DefaultIfZero any
Min any
Max any
MustBeIn []float64
MustNotBeIn []float64
ValidateFunc func(float64) error
SkipFunc func(float64) bool
}
Float describes rules for a float-kinded value or field.
type Int ¶
type Int struct {
MustNotBeNil bool
MustNotBeZero bool
DefaultIfNil any
DefaultIfZero any
Min any
Max any
MustBeIn []int
MustNotBeIn []int
ValidateFunc func(int) error
SkipFunc func(int) bool
}
Int describes rules for a signed-integer-kinded value or field.
type Map ¶
type Map struct {
MustNotBeNil bool
DefaultIfNil any
KeySchema Schema
ValueSchema Schema
MinLen any
MaxLen any
ValidateFunc func(int) error
SkipFunc func(int) bool
}
Map describes rules for a map-kinded value or field.
If KeySchema is non-nil, it is applied to every key. If ValueSchema is non-nil, it is applied to every value. Both run before any Schema() method declared by the key or value type.
MinLen and MaxLen are value-level constraints and fire on a non-nil empty map. Map does not use zero vocabulary; emptiness is handled by MinLen.
type Object ¶
Object is the schema for a struct or a string-keyed map. The string keys are either field names (when applied to a struct) or map keys, with reserved sentinel keys for object-level callbacks.
type Result ¶
type Result[T any] struct { Value T }
Result wraps the transformed output of Enforce. Callers access res.Value to read the output. The wrapper exists so that chaining and re-assignment do not accidentally shadow the input variable.
type ResultAny ¶
type ResultAny struct {
Value any
}
ResultAny is the type-erased form of Result.
func EnforceAny ¶
EnforceAny is the type-erased form of Enforce. If root is supplied, it is applied at the root in addition to any discovered Schema() method on the root value. The discovered schema runs first, then the explicit root.
type Schema ¶
type Schema interface {
// contains filtered or unexported methods
}
Schema is the interface that all schema return values satisfy: rule blocks (String, Int, ...) for newtype schemas and Object for struct / string-keyed-map schemas.
type SchemaError ¶
type SchemaError struct{ Err error }
SchemaError is returned when the schema itself is malformed (e.g., it references a field that does not exist on the target type, or applies a rule block to a field of the wrong kind). These are bugs in the schema, not failures of the data being validated.
func (*SchemaError) Error ¶
func (e *SchemaError) Error() string
func (*SchemaError) Unwrap ¶
func (e *SchemaError) Unwrap() error
type Schematic ¶
type Schematic interface {
Schema() Schema
}
Schematic is implemented by types that describe their own schema.
type Slice ¶
type Slice struct {
MustNotBeNil bool
DefaultIfNil any
ElementSchema Schema
MinLen any
MaxLen any
ValidateFunc func(int) error
SkipFunc func(int) bool
}
Slice describes rules for a slice- or array-kinded value or field.
If ElementSchema is non-nil, it is applied to every element. Any Schema() method declared by the element type is also applied, via recursive discovery during the outer walk.
MinLen and MaxLen are value-level constraints and fire on a non-nil empty slice. Slice does not use zero vocabulary; emptiness is handled by MinLen.
type String ¶
type String struct {
// Nil / zero controls.
MustNotBeNil bool
MustNotBeZero bool
DefaultIfNil any
DefaultIfZero any
// Normalizations are applied in order: TrimSpace, case, TransformFunc.
TrimSpace bool
ToLower bool
ToUpper bool
TransformFunc func(string) (string, error)
// Validators. MinLen and MaxLen accept any integer-kinded value.
MinLen any
MaxLen any
MustBeIn []string
MustNotBeIn []string
MustBeEmail bool
MustBeURL bool
MustMatch *regexp.Regexp
MustStartWith string
MustEndWith string
AllowedChars string
// ValidateFunc runs after built-in validators on the final
// post-normalization value.
ValidateFunc func(string) error
// SkipFunc, if non-nil and returns true, causes this rule to be
// skipped entirely for this value.
SkipFunc func(string) bool
}
String describes rules for a string-kinded value or field.
type Uint ¶
type Uint struct {
MustNotBeNil bool
MustNotBeZero bool
DefaultIfNil any
DefaultIfZero any
Min any
Max any
MustBeIn []uint
MustNotBeIn []uint
ValidateFunc func(uint) error
SkipFunc func(uint) bool
}
Uint describes rules for an unsigned-integer-kinded value or field.
type ValidationError ¶
type ValidationError struct{ Err error }
ValidationError wraps data-validation failures.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
func (*ValidationError) Unwrap ¶
func (e *ValidationError) Unwrap() error