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.
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.
package x(runtime)Registrywith listener hooks and merge supportTypewrapper withSynteticType *model.Typefor 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
- Go type model,
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/FromRegistryFilecollect registry types into a Namespace/GoFile- Uses
SynteticTypewhen present, otherwise falls back toxreflect.BuildType
- Use
loader/astfor full fidelity from source (generics, aliases, methods, top‑level funcs/consts/vars, embeds). - Use
loader/xreflectwhen you only have runtime types and need to produce declarations; it models structural shapes and exported method sets.
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: The canonical
go.modreferences 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=../afsand drop it before committing, or use a separate-modfile.
- Preferred: use Go workspaces (
- See
DEVELOPMENT.mdfor detailed workflows and examples (includinggo.work.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)
}- 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/transformpackage can adapt or rewrite the model prior to rendering (e.g., tags, renames, type substitutions).