norm

package
v0.502.2 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package norm provides a framework for defining and applying normalization to data structures. It is the normalization counterpart to the rules package: instead of declaring and calling normalizer methods and manually recursing into every property, normalizers are registered against a type and a reflective engine walks the object graph applying the matching ones.

A normalizer is a plain Go function bound to a single type via For. It may be gated by a guard Test (typically built with the is package, e.g. is.InContext(tax.AddonIn(V4))) exactly as guards work in the rules package.

norm is intentionally simpler than rules: normalizers are not identified by code, cannot fail, and there are no field/each sub-trees — the engine discovers nested values by reflection. Normalizers must be idempotent: the engine may apply them more than once when meta-addons append further addons during normalization (see Normalize).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Normalize

func Normalize(doc any, opts ...rules.WithContext)

Normalize applies every registered normalizer that matches a value reachable from doc. doc must be a pointer so that mutations persist. The object graph is walked once, depth-first and post-order: a node's children are normalized before the node's own normalizers run, so addon/regime normalizers always see fully-normalized children.

Guards are evaluated against a validation context built from opts and from the root's embedded rules.ContextAdder fields (tax.Regime, tax.Addons), so context guards such as is.InContext(tax.AddonIn(V4)) apply to nested values without needing access to the root. The complete addon set (declared plus dependencies) is resolved before the walk via prepare; normalizers cannot add further addons during normalization.

func Register

func Register(sets ...*Set)

Register adds one or more normalizers to the global registry. Normalizers are matched purely by the type they target, so no namespace or package label is required.

func RegisterWithGuard

func RegisterWithGuard(guard rules.Test, sets ...*Set)

RegisterWithGuard adds one or more normalizers behind a shared guard. The guard is typically a context test such as is.InContext(tax.AddonIn(V4)) or is.InContext(tax.RegimeIn("ES")) so that the normalizers only run for documents using that addon or regime.

Types

type Func

type Func func(obj any)

Func is a normalizer bound to a concrete type by For. It receives a pointer to the value being normalized so that mutations persist.

type Set

type Set struct {
	// contains filtered or unexported fields
}

Set binds a normalizer to a type (via For), or groups guarded subsets (via When). It is the building block passed to Register and RegisterWithGuard. Unlike rules.Set there are no IDs, assertions, or field/each subsets.

func For

func For[T any](fn func(*T)) *Set

For binds a typed normalizer to its type. The target type T is inferred from the function signature, so each normalizer targets exactly one type and no type switch is required.

The function is always called with a non-nil pointer to the value being normalized: the engine skips nil pointers, nil interfaces, and nil map values while traversing, so normalizers do not need their own nil checks.

norm.For(func(inv *bill.Invoice) {
    if inv.Type == cbc.KeyEmpty {
        inv.Type = bill.InvoiceTypeStandard
    }
})

The document is walked once, depth-first and post-order (children before the node itself). A normalizer that creates a previously-nil sub-object or appends new elements is responsible for normalizing them itself — those new nodes are not revisited by the engine in the same pass, so type-bound and global normalizers will not run on them automatically.

func When

func When(guard rules.Test, sets ...*Set) *Set

When wraps sets so that they only run when guard passes. It composes with any registration-level guard (RegisterWithGuard): all guards along the path from registration to a normalizer must pass for it to run. Guards are evaluated against the same value (a pointer to the node) and may inspect the validation context when they implement rules.ContextualTest, so context guards such as is.InContext(tax.AddonIn(V4)) work unchanged.

Jump to

Keyboard shortcuts

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