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 ¶
- func JoinUnionTerms(terms []model.Term) string
- func NormalizeUnionTerms(terms []model.Term) []string
- type BuildOption
- func WithAliasReporter(fn func(alias *model.Alias, resolved reflect.Type)) BuildOption
- func WithAllowAnonymousRecursion(allow bool) BuildOption
- func WithAllowInterface(allow bool) BuildOption
- func WithAnonymousRecursionReporter(fn func(name string)) BuildOption
- func WithCache(cache map[string]reflect.Type) BuildOption
- func WithInterfaceResolver(r InterfaceResolver) BuildOption
- func WithResolver(r Resolver) BuildOption
- func WithStrictInterfaceResolution(strict bool) BuildOption
- func WithStrictNamedResolution(strict bool) BuildOption
- func WithStrictUnionResolution(strict bool) BuildOption
- func WithTransforms(t trf.Transformer) BuildOption
- func WithUnknownFallback(fn func() reflect.Type) BuildOption
- func WithUnresolvedInterfaceReporter(fn func(methods []model.Method)) BuildOption
- func WithUnresolvedNamedReporter(fn func(pkgPath, name string, candidates []string)) BuildOption
- func WithUnresolvedUnionReporter(fn func(terms []model.Term)) BuildOption
- type Builder
- type InterfaceResolver
- type Resolver
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func JoinUnionTerms ¶
JoinUnionTerms returns union terms joined with " | " for diagnostics.
func NormalizeUnionTerms ¶
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.
type InterfaceResolver ¶
InterfaceResolver resolves model.Interface method sets to compiled interface types. Implementations may use method-set fingerprints or registry lookups.