xreflect

package
v0.4.0 Latest Latest
Warning

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

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

Documentation

Overview

Package xreflect provides a stateful builder that materializes runtime reflect.Type values from the syntetic/model Node graph. It complements the loader/xreflect package (which reads from reflect) by handling the inverse direction: model → reflect.

Limitations

  • New named types cannot be created at runtime; Named nodes are resolved via a Resolver to existing compiled types. When unresolved, the builder falls back to a policy-driven Unknown handler (by default: interface{}). Resolution precedence for model.Named is: 1. Resolver/cache hit 2. Underlying node materialization (if provided) 3. strict-mode error or tolerant-mode unknown fallback Strict behavior is enabled with WithStrictNamedResolution(true). Optional diagnostics callback can be attached with WithUnresolvedNamedReporter.
  • Anonymous recursive composites (e.g., a dynamic struct that refers to itself) are rejected by default. Tolerant mode can be enabled with WithAllowAnonymousRecursion(true), which resolves recursive edges via unknown fallback and optionally reports synthetic recursion identifiers via WithAnonymousRecursionReporter.
  • Generics and union constraints have no runtime representation and are represented as unknown fallback in tolerant mode, or rejected in strict mode via WithStrictUnionResolution(true). Diagnostics callback is available via WithUnresolvedUnionReporter.
  • Alias nodes materialize to their target runtime type. Alias declaration metadata can be observed via WithAliasReporter for codegen/docs layers.
  • Methods cannot be attached to dynamic structs; InterfaceOf support is limited and policy-gated. Non-empty model.Interface nodes are resolved through an optional InterfaceResolver. WithStrictInterfaceResolution(true) makes unresolved non-empty interfaces fail fast; tolerant mode falls back to interface{}.
Example (AliasReporter)

Example: alias reporter captures declaration metadata while runtime uses target type.

package main

import (
	"fmt"
	"reflect"

	xr "github.com/viant/x/builder/xreflect"
	mdl "github.com/viant/x/syntetic/model"
)

func main() {
	alias := &mdl.Alias{
		PkgPath: "example.com/p",
		Name:    "IDs",
		Target:  &mdl.Slice{Elem: &mdl.Basic{Name: "int"}},
	}

	var captured string
	b := xr.New(
		xr.WithAliasReporter(func(a *mdl.Alias, resolved reflect.Type) {
			captured = a.PkgPath + "." + a.Name + " -> " + resolved.String()
		}),
	)
	rt, _ := b.BuildNode(alias)
	fmt.Println(rt.String())
	fmt.Println(captured)
}
Output:

[]int
example.com/p.IDs -> []int
Example (FromTags)

Example: tag-driven field rewrites with a resolver mapping named types.

package main

import (
	"fmt"
	"reflect"

	xr "github.com/viant/x/builder/xreflect"
	mdl "github.com/viant/x/syntetic/model"

	trf "github.com/viant/x/syntetic/model/transform"
)

type exResolver map[string]reflect.Type

func (m exResolver) Resolve(pkgPath, name string) (reflect.Type, bool) {
	t, ok := m[pkgPath+"."+name]
	return t, ok
}

func main() {
	// Build a model.Struct with tags that instruct codegen:
	// - omit field A
	// - rename field B to R, set tag json:"r", and set type to ex/p.Alias
	n := &mdl.Struct{Fields: []mdl.Field{
		{Name: "A", Tag: `x:"omit"`, Type: &mdl.Basic{Name: "int"}},
		{Name: "B", Tag: `x:"rename=R,tag=json:\"r\",type=ex/p.Alias"`, Type: &mdl.Basic{Name: "string"}},
	}}

	// Map ex/p.Alias to a concrete runtime type.
	res := exResolver{"ex/p.Alias": reflect.TypeOf(uint16(0))}
	b := xr.New(xr.WithResolver(res), xr.WithTransforms(trf.FromTags()))

	rt, _ := b.BuildNode(n)
	f := rt.Field(0)
	fmt.Println(f.Name, f.Type.String(), string(f.Tag))
}
Output:

R uint16 json:"r"
Example (InterfaceResolverStrict)

Example: strict interface resolution with a method-set resolver.

package main

import (
	"fmt"
	"reflect"

	xr "github.com/viant/x/builder/xreflect"
	mdl "github.com/viant/x/syntetic/model"
)

type exInterfaceResolver struct {
	t reflect.Type
}

func (r exInterfaceResolver) ResolveInterface(methods []mdl.Method) (reflect.Type, bool) {
	if len(methods) == 1 && methods[0].Name == "Read" {
		return r.t, true
	}
	return nil, false
}

func main() {
	type Reader interface {
		Read([]byte) (int, error)
	}

	n := &mdl.Interface{
		Methods: []mdl.Method{
			{
				Name: "Read",
				Type: mdl.Func{
					Params: []mdl.Field{{Type: &mdl.Slice{Elem: &mdl.Basic{Name: "byte"}}}},
					Results: []mdl.Field{
						{Type: &mdl.Basic{Name: "int"}},
						{Type: &mdl.Basic{Name: "error"}},
					},
				},
			},
		},
	}

	want := reflect.TypeOf((*Reader)(nil)).Elem()
	b := xr.New(
		xr.WithInterfaceResolver(exInterfaceResolver{t: want}),
		xr.WithStrictInterfaceResolution(true),
	)
	rt, _ := b.BuildNode(n)
	fmt.Println(rt == want)
}
Output:

true
Example (StrictUnionResolution)

Example: strict union/type-set handling reports unsupported runtime shape.

package main

import (
	"fmt"

	xr "github.com/viant/x/builder/xreflect"
	mdl "github.com/viant/x/syntetic/model"
)

func main() {
	n := &mdl.Union{
		Terms: []mdl.Term{
			{Type: &mdl.Basic{Name: "int"}, Approx: true},
			{Type: &mdl.Basic{Name: "string"}},
		},
	}

	b := xr.New(xr.WithStrictUnionResolution(true))
	_, err := b.BuildNode(n)
	fmt.Println(err != nil)
}
Output:

true

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func JoinUnionTerms

func JoinUnionTerms(terms []model.Term) string

JoinUnionTerms returns union terms joined with " | " for diagnostics.

func NormalizeUnionTerms

func NormalizeUnionTerms(terms []model.Term) []string

NormalizeUnionTerms stringifies union terms using stable formatting. Example output items: "~int", "string", "*example.com/p.Type".

Types

type BuildOption

type BuildOption func(*Builder)

BuildOption configures the Builder.

func WithAliasReporter

func WithAliasReporter(fn func(alias *model.Alias, resolved reflect.Type)) BuildOption

WithAliasReporter installs callback for alias metadata preservation. Alias nodes materialize to their target runtime type, while this callback exposes alias declaration metadata to callers.

func WithAllowAnonymousRecursion

func WithAllowAnonymousRecursion(allow bool) BuildOption

WithAllowAnonymousRecursion controls handling of anonymous recursive structs. false (default): return error on recursive anonymous struct edge. true: materialize recursive edge via unknown() fallback and continue.

func WithAllowInterface

func WithAllowInterface(allow bool) BuildOption

WithAllowInterface enables attempting to construct interface types via reflect.InterfaceOf when method sets are present. By default the builder falls back to interface{} for non-empty interfaces to avoid portability issues.

func WithAnonymousRecursionReporter

func WithAnonymousRecursionReporter(fn func(name string)) BuildOption

WithAnonymousRecursionReporter installs callback for synthetic recursion names emitted when anonymous recursion is tolerated.

func WithCache

func WithCache(cache map[string]reflect.Type) BuildOption

WithCache provides an external cache for named type resolutions to improve reuse across builds and break cycles. Keys are "pkgPath.name".

func WithInterfaceResolver

func WithInterfaceResolver(r InterfaceResolver) BuildOption

WithInterfaceResolver installs resolver for non-empty model.Interface nodes.

func WithResolver

func WithResolver(r Resolver) BuildOption

WithResolver installs a Named type resolver.

func WithStrictInterfaceResolution

func WithStrictInterfaceResolution(strict bool) BuildOption

WithStrictInterfaceResolution controls behavior for unresolved non-empty interfaces. false (default): fallback to interface{}. true: return an error when interface resolver cannot resolve method set.

func WithStrictNamedResolution

func WithStrictNamedResolution(strict bool) BuildOption

WithStrictNamedResolution controls behavior for unresolved model.Named nodes. When enabled, BuildNode returns an error instead of falling back to unknown().

func WithStrictUnionResolution

func WithStrictUnionResolution(strict bool) BuildOption

WithStrictUnionResolution controls behavior for model.Union nodes. false (default): fallback to unknown(). true: return an error for union/type-set nodes (no native runtime type).

func WithTransforms

func WithTransforms(t trf.Transformer) BuildOption

WithTransforms configures a transformer applied to nodes before materializing reflect.Type. This allows tag-driven rewrites or custom node adjustments to be applied uniformly.

func WithUnknownFallback

func WithUnknownFallback(fn func() reflect.Type) BuildOption

WithUnknownFallback sets the function used when a node cannot be materialized (e.g., unresolved Named). Default returns interface{}.

func WithUnresolvedInterfaceReporter

func WithUnresolvedInterfaceReporter(fn func(methods []model.Method)) BuildOption

WithUnresolvedInterfaceReporter installs callback for unresolved non-empty interfaces. It is invoked when strict interface resolution is enabled and method-set resolution fails.

func WithUnresolvedNamedReporter

func WithUnresolvedNamedReporter(fn func(pkgPath, name string, candidates []string)) BuildOption

WithUnresolvedNamedReporter installs a callback for unresolved named type diagnostics. It is invoked only when strict named resolution is enabled and a name cannot be resolved.

func WithUnresolvedUnionReporter

func WithUnresolvedUnionReporter(fn func(terms []model.Term)) BuildOption

WithUnresolvedUnionReporter installs callback for union/type-set diagnostics. It is invoked when strict union resolution is enabled.

type Builder

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

Builder materializes reflect.Type from model.Node values using reflect's *Of constructors. It is stateful to hold caches and policies.

func New

func New(opts ...BuildOption) *Builder

New creates a new Builder with optional configuration.

func (*Builder) BuildNode

func (b *Builder) BuildNode(n model.Node) (reflect.Type, error)

BuildNode converts a model.Node to a reflect.Type according to builder policies. It returns an error for shapes that cannot be expressed at runtime (e.g., anonymous recursive composites).

type InterfaceResolver

type InterfaceResolver interface {
	ResolveInterface(methods []model.Method) (reflect.Type, bool)
}

InterfaceResolver resolves model.Interface method sets to compiled interface types. Implementations may use method-set fingerprints or registry lookups.

type Resolver

type Resolver interface {
	Resolve(pkgPath, name string) (reflect.Type, bool)
}

Resolver maps a (pkgPath, name) to a compiled reflect.Type when available. It is used to resolve Named nodes to existing types since new named types cannot be constructed at runtime.

Jump to

Keyboard shortcuts

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