debug: Expose list of components to frontend
This commit is contained in:
parent
e17ab90269
commit
e5cd57cb7d
14 changed files with 307 additions and 14 deletions
|
|
@ -2,7 +2,6 @@ package lazy
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Pool represents a pool of laziliy initialized and potentially referencing Component instances.
|
||||
|
|
@ -19,6 +18,9 @@ type Pool[Component any, InitParams any] struct {
|
|||
// Init is called on every component to be initialized.
|
||||
Init func(Component, InitParams) Component
|
||||
|
||||
// Analytics are written on the first retrieval operation on this Pool
|
||||
Analytics PoolAnalytics
|
||||
|
||||
all Lazy[[]Component]
|
||||
}
|
||||
|
||||
|
|
@ -37,12 +39,16 @@ func (p *Pool[Component, InitParams]) All(Params InitParams, All func(context *P
|
|||
}
|
||||
return p.Init(c, Params)
|
||||
},
|
||||
cache: make(map[string]Component),
|
||||
metaCache: make(map[reflect.Type]meta[Component]),
|
||||
cache: make(map[string]Component),
|
||||
}
|
||||
|
||||
// and process them all
|
||||
all := context.all(context)
|
||||
context.Process(all)
|
||||
context.process(all)
|
||||
|
||||
// write out analytics
|
||||
context.anal(&p.Analytics)
|
||||
return all
|
||||
})
|
||||
}
|
||||
|
|
@ -56,7 +62,7 @@ type PoolContext[Component any] struct {
|
|||
|
||||
// function to return all components
|
||||
|
||||
metaCache sync.Map // Map[string]meta[Component]
|
||||
metaCache map[reflect.Type]meta[Component]
|
||||
cache map[string]Component // cached components
|
||||
queue []delayedInit[Component] // init queue
|
||||
}
|
||||
|
|
@ -67,7 +73,7 @@ type delayedInit[Component any] struct {
|
|||
}
|
||||
|
||||
// Process processes all components in the queue
|
||||
func (p *PoolContext[Component]) Process(all []Component) {
|
||||
func (p *PoolContext[Component]) process(all []Component) {
|
||||
index := 0
|
||||
for len(p.queue) > index {
|
||||
p.queue[index].Run(all)
|
||||
|
|
@ -97,7 +103,7 @@ func (di *delayedInit[Component]) Run(all []Component) {
|
|||
// The init function may be nil, indicating that no additional initialization is required.
|
||||
func Make[Component any, ConcreteComponent any](context *PoolContext[Component], init func(component ConcreteComponent)) ConcreteComponent {
|
||||
// get a description of the type
|
||||
cd := getMeta[Component, ConcreteComponent](&context.metaCache)
|
||||
cd := getMeta[Component, ConcreteComponent](context.metaCache)
|
||||
|
||||
// if an instance already exists, return it!
|
||||
if instance, ok := context.cache[cd.Name]; ok {
|
||||
|
|
|
|||
93
pkg/lazy/pool_anal.go
Normal file
93
pkg/lazy/pool_anal.go
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package lazy
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/tkw1536/goprogram/lib/collection"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type PoolAnalytics struct {
|
||||
Components map[string]*PoolAnalyticsComponent
|
||||
Groups map[string]*PoolAnalyticsGroup
|
||||
}
|
||||
|
||||
type PoolAnalyticsComponent struct {
|
||||
Type string // Type name
|
||||
Groups []string // groups this is contained in
|
||||
|
||||
CFields map[string]string // fields with type C for which C implements component
|
||||
IFields map[string]string // fields []I where I is an interface that implements component
|
||||
|
||||
Methods map[string]string // Method signatures of type
|
||||
}
|
||||
type PoolAnalyticsGroup struct {
|
||||
Type string // Type name
|
||||
Components []string // Components of this Type
|
||||
|
||||
Methods map[string]string // Method signatures of this interface
|
||||
}
|
||||
|
||||
// anal writes analytics about this context to anal
|
||||
func (context *PoolContext[Component]) anal(anal *PoolAnalytics) {
|
||||
anal.Components = make(map[string]*PoolAnalyticsComponent, len(context.metaCache))
|
||||
anal.Groups = make(map[string]*PoolAnalyticsGroup)
|
||||
|
||||
// collect all the pointers, and setup the anal.Components map!
|
||||
tpPointers := make([]reflect.Type, 0, len(context.metaCache))
|
||||
for _, meta := range context.metaCache {
|
||||
tp := reflect.PointerTo(meta.Elem)
|
||||
tpPointers = append(tpPointers, tp)
|
||||
|
||||
mcount := tp.NumMethod()
|
||||
|
||||
anal.Components[meta.Name] = &PoolAnalyticsComponent{
|
||||
Groups: make([]string, 0),
|
||||
Methods: make(map[string]string, mcount),
|
||||
}
|
||||
for i := 0; i < mcount; i++ {
|
||||
method := tp.Method(i)
|
||||
anal.Components[meta.Name].Methods[method.Name] = method.Type.String()
|
||||
}
|
||||
}
|
||||
|
||||
// take all of the components out of the cache
|
||||
for _, meta := range context.metaCache {
|
||||
anal.Components[meta.Name].Type = meta.Name
|
||||
anal.Components[meta.Name].CFields = collection.MapValues(meta.CFields, func(key string, tp reflect.Type) string {
|
||||
return nameOf(tp.Elem())
|
||||
})
|
||||
|
||||
anal.Components[meta.Name].IFields = collection.MapValues(meta.IFields, func(key string, iface reflect.Type) string {
|
||||
name := nameOf(iface)
|
||||
|
||||
if _, ok := anal.Groups[name]; !ok {
|
||||
types := collection.FilterClone(tpPointers, func(tp reflect.Type) bool {
|
||||
return tp.AssignableTo(iface)
|
||||
})
|
||||
|
||||
anal.Groups[name] = &PoolAnalyticsGroup{
|
||||
Type: name,
|
||||
Components: collection.MapSlice(types, func(tp reflect.Type) string {
|
||||
cname := nameOf(tp.Elem())
|
||||
anal.Components[cname].Groups = append(anal.Components[cname].Groups, name)
|
||||
return cname
|
||||
}),
|
||||
}
|
||||
|
||||
mcount := iface.NumMethod()
|
||||
anal.Groups[name].Methods = make(map[string]string, mcount)
|
||||
for i := 0; i < mcount; i++ {
|
||||
method := iface.Method(i)
|
||||
anal.Groups[name].Methods[method.Name] = method.Type.String()
|
||||
}
|
||||
}
|
||||
|
||||
return name
|
||||
})
|
||||
}
|
||||
|
||||
for _, comp := range anal.Components {
|
||||
slices.Sort(comp.Groups)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,19 +2,18 @@ package lazy
|
|||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/tkw1536/goprogram/lib/collection"
|
||||
"github.com/tkw1536/goprogram/lib/reflectx"
|
||||
)
|
||||
|
||||
// getMeta gets the component belonging to a component type
|
||||
func getMeta[Component any, ConcreteComponent any](metaCache *sync.Map) meta[Component] {
|
||||
func getMeta[Component any, ConcreteComponent any](cache map[reflect.Type]meta[Component]) meta[Component] {
|
||||
tp := reflectx.TypeOf[ConcreteComponent]()
|
||||
|
||||
// we already have a m => return it
|
||||
if m, ok := metaCache.Load(tp); ok {
|
||||
return m.(meta[Component])
|
||||
if m, ok := cache[tp]; ok {
|
||||
return m
|
||||
}
|
||||
|
||||
// create a new m
|
||||
|
|
@ -22,7 +21,7 @@ func getMeta[Component any, ConcreteComponent any](metaCache *sync.Map) meta[Com
|
|||
m.init(tp)
|
||||
|
||||
// store it in the cache
|
||||
metaCache.Store(tp, m)
|
||||
cache[tp] = m
|
||||
return m
|
||||
}
|
||||
|
||||
|
|
@ -43,8 +42,8 @@ func (m *meta[Component]) init(tp reflect.Type) {
|
|||
panic("GetMeta: Type (" + tp.String() + ") must be backed by a pointer to slice")
|
||||
}
|
||||
|
||||
m.Name = tp.Elem().PkgPath() + "." + tp.Elem().Name()
|
||||
m.Elem = tp.Elem()
|
||||
m.Name = nameOf(m.Elem)
|
||||
|
||||
m.CFields = make(map[string]reflect.Type)
|
||||
m.IFields = make(map[string]reflect.Type)
|
||||
|
|
@ -69,6 +68,10 @@ func (m *meta[Component]) init(tp reflect.Type) {
|
|||
}
|
||||
}
|
||||
|
||||
func nameOf(tp reflect.Type) string {
|
||||
return tp.PkgPath() + "." + tp.Name()
|
||||
}
|
||||
|
||||
// New creates a new ComponentDescription
|
||||
func (m meta[Component]) New() Component {
|
||||
return reflect.New(m.Elem).Interface().(Component)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue