schema

package
v0.85.0-pre.13 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2026 License: BSD-3-Clause Imports: 10 Imported by: 0

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

View Source
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

func IsSchemaError(err error) bool

IsSchemaError reports whether err is or wraps a SchemaError.

func IsValidationError

func IsValidationError(err error) bool

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 List

type List = Slice

List is an alias for Slice.

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

type Object map[string]any

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.

func Enforce

func Enforce[T any](label string, value T, root ...Schema) (Result[T], error)

Enforce runs any schema declared by value and returns the final transformed value wrapped in Result.

type ResultAny

type ResultAny struct {
	Value any
}

ResultAny is the type-erased form of Result.

func EnforceAny

func EnforceAny(label string, value any, root ...Schema) (ResultAny, error)

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

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL