147 lines
3.8 KiB
Go
147 lines
3.8 KiB
Go
package component
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
)
|
|
|
|
// Pool holds a pool of components and provides factilities to create and access them.
|
|
// See [Pool.All], [ExportComponents] and [ExportComponent].
|
|
type Pool struct {
|
|
all lazy.Lazy[[]Component] // all components
|
|
}
|
|
|
|
// All initializes or returns all components stored in this pool.
|
|
//
|
|
// The All function should return an array of calls to [Make] with the provided context.
|
|
// Multiple calls to the this method return the same return value.
|
|
func (p *Pool) All(All func(context *PoolContext) []Component) []Component {
|
|
return p.all.Get(func() []Component {
|
|
// create a new context
|
|
context := &PoolContext{
|
|
all: All,
|
|
cache: make(map[string]Component),
|
|
}
|
|
|
|
// and process them all
|
|
all := context.all(context)
|
|
context.process(all)
|
|
return all
|
|
})
|
|
}
|
|
|
|
// PoolContext is a context used during [Make].
|
|
// It should not be initialized by a user.
|
|
type PoolContext struct {
|
|
all func(context *PoolContext) []Component // function to return all components
|
|
|
|
cache map[string]Component // cached components
|
|
queue []delayedInit // init queue
|
|
}
|
|
|
|
type delayedInit struct {
|
|
meta meta
|
|
value reflect.Value
|
|
}
|
|
|
|
func (di delayedInit) Do(all []Component) {
|
|
di.meta.InitComponent(di.value, all)
|
|
}
|
|
|
|
// process processes the queue in this process
|
|
func (p *PoolContext) process(all []Component) {
|
|
index := 0
|
|
for len(p.queue) > index {
|
|
p.queue[index].Do(all)
|
|
index++
|
|
}
|
|
p.queue = nil
|
|
}
|
|
|
|
// Make creates or returns a cached component of the given Context.
|
|
//
|
|
// Components are initialized by first calling the init function.
|
|
// Then all component-like fields of fields are filled with their appropriate components.
|
|
//
|
|
// A component-like field has one of the following types:
|
|
//
|
|
// - A pointer to a struct type that implements component
|
|
// - A slice type of an interface type that implements component
|
|
//
|
|
// These fields are initialized in an undefined order during initialization.
|
|
// The init function may not rely on these existing.
|
|
// Furthermore, the init function may not cause other components to be initialized.
|
|
//
|
|
// The init function may be nil, indicating that no additional initialization is required.
|
|
func Make[C Component](context *PoolContext, core Core, init func(component C)) C {
|
|
// get a description of the type
|
|
cd := getMeta[C]()
|
|
|
|
// if an instance already exists, return it!
|
|
if instance, ok := context.cache[cd.Name]; ok {
|
|
return instance.(C)
|
|
}
|
|
|
|
// make sure that we have an array of components
|
|
if context.cache == nil {
|
|
context.cache = make(map[string]Component)
|
|
}
|
|
|
|
// create a fresh (empty) instance
|
|
context.cache[cd.Name] = cd.New()
|
|
instance := context.cache[cd.Name].(C)
|
|
|
|
// do the core and self-initialization
|
|
instance.getBase().Core = core
|
|
|
|
if init != nil {
|
|
init(instance)
|
|
}
|
|
|
|
if cd.NeedsInitComponent() {
|
|
context.queue = append(context.queue, delayedInit{
|
|
meta: cd,
|
|
value: reflect.ValueOf(instance),
|
|
})
|
|
}
|
|
|
|
return instance
|
|
}
|
|
|
|
//
|
|
// PUBLIC FUNCTIONS
|
|
//
|
|
|
|
// ExportComponents exports all components that are a C from the pool.
|
|
//
|
|
// All should be the function of the core that initializes all components.
|
|
// All should only make calls to [InitComponent].
|
|
func ExportComponents[C Component](p *Pool, All func(context *PoolContext) []Component) []C {
|
|
components := p.All(All)
|
|
|
|
results := make([]C, 0, len(components))
|
|
for _, comp := range components {
|
|
if cc, ok := comp.(C); ok {
|
|
results = append(results, cc)
|
|
}
|
|
}
|
|
return results
|
|
}
|
|
|
|
// ExportComponent exports the first component that is a C from the pool.
|
|
//
|
|
// All should be the function of the core that initializes all components.
|
|
// All should only make calls to [InitComponent].
|
|
func ExportComponent[C Component](p *Pool, All func(context *PoolContext) []Component) C {
|
|
components := p.All(All)
|
|
|
|
for _, comp := range components {
|
|
if cc, ok := comp.(C); ok {
|
|
return cc
|
|
}
|
|
}
|
|
|
|
var c C
|
|
return c
|
|
}
|