x

package module
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: 4 Imported by: 9

README

x — Types Registry + AST/Reflect Reconciliation

This project provides a small set of building blocks to reconcile Go types across three views of the world:

  • Runtime: a lightweight types registry (package x) holding reflect types and metadata
  • Source/AST: a synthetic model (syntetic/model) used to render compilable Go
  • Reflection: a reflect→AST bridge (loader/xreflect) for runtime‑discovered types

The intent is to make code generation and component composition ergonomic whether types originate from source (AST) or from runtime registries. You can attach synthetic model types directly to registry entries or build them from reflect as needed. A thin bridge then aggregates types and emits Go files with deterministic imports.

Note: An early “placeholder module” workflow (for Go’s plugin story) remains possible via the transient submodule, but the primary focus is the registry + AST/reflect reconciliation.

Quick Start

Register types and attach a prebuilt synthetic type for codegen:

package main

import (
    "reflect"
    x "github.com/viant/x"
    "github.com/viant/x/loader/xreflect"
    "github.com/viant/x/syntetic"
)

type Person struct{ ID int; Name string }

func main() {
    reg := x.NewRegistry()
    st, _ := xreflect.BuildType(reflect.TypeOf(Person{}))
    reg.Register(x.NewType(reflect.TypeOf(Person{}), x.WithSynteticType(st)))

    // Aggregate into a single file and render
    file, _ := syntetic.FromRegistryFile(reg)
    file.PkgName = "example"
    src, _ := file.Render()
    _ = src // write to disk or compile
}

See examples/registry_render for a complete runnable example.

Components

  • package x (runtime)
    • Registry with listener hooks and merge support
    • Type wrapper with SynteticType *model.Type for attached synthetic types
    • Options: WithListener, WithMergeListener, WithSynteticType
  • syntetic/model (source model + rendering)
    • Go type model, GoFile/Package/Module, deterministic imports
    • Renders valid Go via Render/RenderWithOptions
  • loader/ast (source loader)
    • Parses module/packages from fs.FS; discovers types, funcs, consts, vars, embeds
  • loader/xreflect (reflect loader)
    • BuildType (single reflect.Type → model.Type)
    • LoadPackage (group reflect.Types → model.Package)
  • syntetic (bridge)
    • FromRegistry/FromRegistryFile collect registry types into a Namespace/GoFile
    • Uses SynteticType when present, otherwise falls back to xreflect.BuildType

Why two loaders?

  • Use loader/ast for full fidelity from source (generics, aliases, methods, top‑level funcs/consts/vars, embeds).
  • Use loader/xreflect when you only have runtime types and need to produce declarations; it models structural shapes and exported method sets.

Transient module (optional)

You can still wire a transient module into your workspace to make extension types loadable with current Go tooling. This remains optional and may become less relevant once Go improves plugin support.

CI and Local Development

  • CI: The canonical go.mod references published module versions (no local replaces).
  • Local dev against sibling modules (e.g., a local checkout of github.com/viant/afs):
    • Preferred: use Go workspaces (go work init . ../afs) to link local copies.
    • Alternative: temporarily add go mod edit -replace github.com/viant/afs=../afs and drop it before committing, or use a separate -modfile.
  • See DEVELOPMENT.md for detailed workflows and examples (including go.work.example).

Merge Listener Semantics (example)

WithMergeListener installs a factory for a per-merge listener. During Merge, the returned listener is invoked once per merged type and once more with nil to signal completion.

package main

import (
    "fmt"
    "reflect"
    x "github.com/viant/x"
)

func example() {
    // Source registry with a couple of types.
    from := x.NewRegistry()
    from.Register(x.NewType(reflect.TypeOf(struct{ A int }{})))
    from.Register(x.NewType(reflect.TypeOf(struct{ B string }{})))

    // Destination registry with a per-merge listener.
    to := x.NewRegistry()
    var keys []string
    done := false
    x.WithMergeListener(func() x.Listener {
        return func(t *x.Type) {
            if t == nil {
                // End-of-merge sentinel.
                done = true
                return
            }
            keys = append(keys, t.Key())
        }
    })(to) // apply option to existing registry

    to.Merge(from)
    fmt.Println("merged keys:", keys)
    fmt.Println("completed:", done)
}

Notes on fidelity and transforms

  • AST loader (loader/ast) preserves source intent (generics, type sets, aliases vs definitions, functions/consts/vars, embeds) and is best for end‑to‑end codegen.
  • Reflect loader (loader/xreflect) focuses on structural shapes of types and exported method sets; it’s perfect for runtime registries and quick declarations but does not capture everything available in source.
  • The syntetic/model/transform package can adapt or rewrite the model prior to rendering (e.g., tags, renames, type substitutions).

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SimpleType

func SimpleType(typ reflect.Type) reflect.Type

SimpleType returns a simple type, it unwraps pointer, slice, array, and chan

Types

type Listener

type Listener func(t *Type)

Listener is invoked on registration events.

When used with Register (single-type), the registry's configured Listener is called with the registered type. When used during Merge (multi-type), the effective listener is the one returned by the MergeListener factory (if configured) and is called once per merged type. See MergeListener for end-of-merge signalling.

type MergeListener added in v0.3.0

type MergeListener func() Listener

MergeListener is a factory for a per-merge Listener.

Merge obtains a fresh Listener by calling the MergeListener at the start of the merge, then invokes the returned Listener for each merged type. After all types have been processed, the returned Listener is called once with a nil *Type to signal end-of-merge. Implementations MUST tolerate a nil argument and treat it as a completion notification.

type Option

type Option func(t *Type)

Option represent type option

func WithForceFlag

func WithForceFlag() Option

WithForceFlag will force the type to be generated

func WithName

func WithName(name string) Option

WithName is an option to set the name of the type

func WithPkgPath added in v0.3.0

func WithPkgPath(pkg string) Option

WithPkgPath is an option to set the package path of the type

func WithScn

func WithScn(at int) Option

WithScn is an option to set the scn(sequence change number) of the type

func WithSynteticType added in v0.4.0

func WithSynteticType(st *model.Type) Option

WithSynteticType sets a prebuilt synthetic/model.Type on this runtime Type. This allows callers to attach a codegen-ready representation directly.

func WithSyntheticType added in v0.4.0

func WithSyntheticType(st *model.Type) Option

WithSyntheticType is an alias for WithSynteticType with conventional spelling. Kept to improve ergonomics while the package name remains "syntetic".

type Registry

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

Registry represents an extension type

func NewRegistry

func NewRegistry(options ...RegistryOption) *Registry

NewRegistry creates a registry

func (*Registry) ForEach added in v0.4.0

func (r *Registry) ForEach(fn func(key string, t *Type) bool)

ForEach invokes fn for each registered type using a sorted snapshot of Keys. Iteration stops early when fn returns false. Callbacks are invoked without holding internal locks.

func (*Registry) Keys added in v0.4.0

func (r *Registry) Keys() []string

Keys returns a sorted snapshot of registry keys.

The returned slice is detached from the underlying map so callers are free to modify it. Ordering is deterministic for a given registry state but concurrent mutation while iterating is not supported.

func (*Registry) Lookup

func (r *Registry) Lookup(name string) *Type

Lookup returns a type by name

func (*Registry) Merge

func (r *Registry) Merge(registry *Registry)

Merge merges all types from the supplied registry into r.

Listener behaviour:

  • If a MergeListener is configured, Merge calls it once to obtain a per-merge Listener and uses that for all per-type notifications. After processing all types, the per-merge Listener is called once with a nil *Type to signal completion. Implementations MUST tolerate nil.
  • If no MergeListener is configured, Merge uses the registry's general Listener (if any) for per-type notifications and does not emit a final nil sentinel call.

func (*Registry) Register

func (r *Registry) Register(aType *Type)

Register registers a type

func (*Registry) Scn

func (r *Registry) Scn() int

Scn returns registry sequence change number

type RegistryOption

type RegistryOption func(r *Registry)

RegistryOption represent registry option

func WithListener

func WithListener(listener Listener) RegistryOption

WithListener creates a new registry with the specified listener

func WithMergeListener added in v0.3.0

func WithMergeListener(listener MergeListener) RegistryOption

WithMergeListener creates a new registry with the specified listener

Semantics:

  • The provided factory is invoked at the start of Registry.Merge to obtain a per-merge Listener.
  • The returned Listener is called once for each merged type.
  • After all types are processed, the returned Listener is called once with a nil *Type to signal completion. Implementations MUST handle nil.
  • If no MergeListener is configured, Merge falls back to the registry's general Listener (configured via WithListener) without the nil sentinel.

func WithRegistryScn

func WithRegistryScn(scn int) RegistryOption

WithRegistryScn is an option to set scn time

type Type

type Type struct {
	Type    reflect.Type
	PkgPath string
	// Location is kept for backward compatibility with earlier x.Type shape.
	Location   string
	Name       string
	Definition string
	Scn        int
	Force      bool
	// SynteticType optionally carries a prebuilt synthetic/model Type
	// associated with this runtime type. This allows callers to attach
	// a codegen-ready representation without re-deriving it via bridges.
	SynteticType *model.Type
	// contains filtered or unexported fields
}

Type represents a type

func NewType

func NewType(t reflect.Type, options ...Option) *Type

NewType creates a type

func (*Type) IsNamed

func (t *Type) IsNamed() bool

IsNamed returns true if type is named

func (*Type) Key

func (t *Type) Key() string

Directories

Path Synopsis
builder
ast
Package ast provides a thin façade over the syntetic/model AST helpers so you can use a consistent "builder" entrypoint for generating declarations and rendered files.
Package ast provides a thin façade over the syntetic/model AST helpers so you can use a consistent "builder" entrypoint for generating declarations and rendered files.
xreflect
Package xreflect provides a stateful builder that materializes runtime reflect.Type values from the syntetic/model Node graph.
Package xreflect provides a stateful builder that materializes runtime reflect.Type values from the syntetic/model Node graph.
examples
namespace_files command
registry_render command
extension module
loader
ast
Package loader: convert.go contains helpers that convert parsed Go AST expressions and function types into the model's intermediate Node and Func representations, including generic type parameters and alias resolution using import alias maps.
Package loader: convert.go contains helpers that convert parsed Go AST expressions and function types into the model's intermediate Node and Func representations, including generic type parameters and alias resolution using import alias maps.
xreflect
Package xreflect provides a reflect-based loader that builds a model.Package or model.Module from reflect.Type values.
Package xreflect provides a reflect-based loader that builds a model.Package or model.Module from reflect.Type values.
adapter
Package adapter provides small adapters between external storage services (e.g., viant/afs) and Go's standard io/fs interfaces so that loaders can operate against arbitrary backends.
Package adapter provides small adapters between external storage services (e.g., viant/afs) and Go's standard io/fs interfaces so that loaders can operate against arbitrary backends.
model
Package model defines an intermediate, reflect-agnostic representation of Go types used by the loader and code generation pipeline.
Package model defines an intermediate, reflect-agnostic representation of Go types used by the loader and code generation pipeline.
model/transform
Package transform provides a small, composable layer to apply structural rewrites to syntetic/model graphs without polluting the core model or the builders.
Package transform provides a small, composable layer to apply structural rewrites to syntetic/model graphs without polluting the core model or the builders.
transient module

Jump to

Keyboard shortcuts

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