diff --git a/internal/dis/component.go b/internal/dis/component.go index d2230ca..f0edece 100644 --- a/internal/dis/component.go +++ b/internal/dis/component.go @@ -2,8 +2,8 @@ package dis import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/tkw1536/pkglib/collection" + "github.com/tkw1536/pkglib/lifetime" ) // @@ -11,18 +11,19 @@ import ( // func (dis *Distillery) init() { - dis.poolInit.Do(func() { - dis.pool.Init = component.Init - lazy.RegisterPoolGroup[component.Backupable](&dis.pool) - lazy.RegisterPoolGroup[component.Snapshotable](&dis.pool) - lazy.RegisterPoolGroup[component.DistilleryFetcher](&dis.pool) - lazy.RegisterPoolGroup[component.Installable](&dis.pool) - lazy.RegisterPoolGroup[component.Provisionable](&dis.pool) - lazy.RegisterPoolGroup[component.Routeable](&dis.pool) - lazy.RegisterPoolGroup[component.Cronable](&dis.pool) - lazy.RegisterPoolGroup[component.UserDeleteHook](&dis.pool) - lazy.RegisterPoolGroup[component.Table](&dis.pool) - lazy.RegisterPoolGroup[component.Menuable](&dis.pool) + dis.lifetimeInit.Do(func() { + dis.lifetime.Init = component.Init + + lifetime.RegisterGroup[component.Backupable](&dis.lifetime) + lifetime.RegisterGroup[component.Snapshotable](&dis.lifetime) + lifetime.RegisterGroup[component.DistilleryFetcher](&dis.lifetime) + lifetime.RegisterGroup[component.Installable](&dis.lifetime) + lifetime.RegisterGroup[component.Provisionable](&dis.lifetime) + lifetime.RegisterGroup[component.Routeable](&dis.lifetime) + lifetime.RegisterGroup[component.Cronable](&dis.lifetime) + lifetime.RegisterGroup[component.UserDeleteHook](&dis.lifetime) + lifetime.RegisterGroup[component.Table](&dis.lifetime) + lifetime.RegisterGroup[component.Menuable](&dis.lifetime) }) } @@ -33,13 +34,13 @@ func (dis *Distillery) init() { // manual initializes a component from the provided distillery. func manual[C component.Component](init func(component C)) initFunc { return func(context ctx) component.Component { - return lazy.Make(context, init) + return lifetime.Make(context, init) } } // use is like r, but does not provided additional initialization func auto[C component.Component](context ctx) component.Component { - return lazy.Make[component.Component, C](context, nil) + return lifetime.Make[component.Component, C](context, nil) } // register returns all components of the distillery @@ -53,7 +54,7 @@ func (dis *Distillery) register(context ctx) []component.Component { } // ctx is a context for component initialization -type ctx = *lazy.PoolContext[component.Component] +type ctx = *lifetime.InjectorContext[component.Component] // // ==== export ==== @@ -62,12 +63,12 @@ type ctx = *lazy.PoolContext[component.Component] // export is a convenience function to export a single component func export[C component.Component](dis *Distillery) C { dis.init() - return lazy.ExportComponent[component.Component, component.Still, C](&dis.pool, dis.Still, dis.register) + return lifetime.ExportComponent[component.Component, component.Still, C](&dis.lifetime, dis.Still, dis.register) } func exportAll[C component.Component](dis *Distillery) []C { dis.init() - return lazy.ExportComponents[component.Component, component.Still, C](&dis.pool, dis.Still, dis.register) + return lifetime.ExportComponents[component.Component, component.Still, C](&dis.lifetime, dis.Still, dis.register) } type initFunc = func(context ctx) component.Component diff --git a/internal/dis/component/auth/auth.go b/internal/dis/component/auth/auth.go index 9e4eabb..38604d0 100644 --- a/internal/dis/component/auth/auth.go +++ b/internal/dis/component/auth/auth.go @@ -7,9 +7,9 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/gorilla/sessions" "github.com/julienschmidt/httprouter" + "github.com/tkw1536/pkglib/lazy" ) type Auth struct { diff --git a/internal/dis/component/component.go b/internal/dis/component/component.go index 476a200..a46d67e 100644 --- a/internal/dis/component/component.go +++ b/internal/dis/component/component.go @@ -11,7 +11,7 @@ import ( // Components represents a logical subsystem of the distillery. // A Component should be implemented as a pointer to a struct. -// Every component must embed [Base] and should be initialized using [Init] inside a [lazy.Pool]. +// Every component must embed [Base] and should be initialized using [Init] inside a [lifetime.Lifetime]. // // By convention these are defined within their corresponding subpackage. // This subpackage also contains all required resources. @@ -41,7 +41,7 @@ func (cb *Base) getBase() *Base { } // Init initialzes a new componeont Component with the provided still. -// Init is only initended to be used within a lazy.Pool[Component,Still]. +// Init is only initended to be used within a lifetime.Lifetime[Component,Still]. func Init(component Component, core Still) Component { base := component.getBase() // pointer to a struct base.Still = core diff --git a/internal/dis/component/resolver/resolver.go b/internal/dis/component/resolver/resolver.go index 0062378..15a74e6 100644 --- a/internal/dis/component/resolver/resolver.go +++ b/internal/dis/component/resolver/resolver.go @@ -15,8 +15,8 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/rs/zerolog" + "github.com/tkw1536/pkglib/lazy" _ "embed" ) diff --git a/internal/dis/component/server/admin/admin.go b/internal/dis/component/server/admin/admin.go index 7e60082..79f34c3 100644 --- a/internal/dis/component/server/admin/admin.go +++ b/internal/dis/component/server/admin/admin.go @@ -11,10 +11,10 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/julienschmidt/httprouter" "github.com/rs/zerolog" + "github.com/tkw1536/pkglib/lifetime" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" ) type Admin struct { @@ -33,7 +33,7 @@ type Admin struct { Sockets *socket.Sockets } - Analytics *lazy.PoolAnalytics + Analytics *lifetime.Analytics } var ( diff --git a/internal/dis/component/server/admin/components.go b/internal/dis/component/server/admin/components.go index 6feef93..985ad2e 100644 --- a/internal/dis/component/server/admin/components.go +++ b/internal/dis/component/server/admin/components.go @@ -12,8 +12,8 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/assets" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/pkg/httpx" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/julienschmidt/httprouter" + "github.com/tkw1536/pkglib/lifetime" ) //go:embed "html/anal.html" @@ -27,7 +27,7 @@ var analTemplate = templating.Parse[analContext]( type analContext struct { templating.RuntimeFlags - Analytics lazy.PoolAnalytics + Analytics lifetime.Analytics } func (admin *Admin) components(ctx context.Context) http.Handler { diff --git a/internal/dis/component/server/home/home.go b/internal/dis/component/server/home/home.go index c7f775c..0b518e5 100644 --- a/internal/dis/component/server/home/home.go +++ b/internal/dis/component/server/home/home.go @@ -9,7 +9,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating" "github.com/FAU-CDI/wisski-distillery/internal/status" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" + "github.com/tkw1536/pkglib/lazy" ) type Home struct { diff --git a/internal/dis/component/server/templating/templating.go b/internal/dis/component/server/templating/templating.go index e53efd7..434ec1b 100644 --- a/internal/dis/component/server/templating/templating.go +++ b/internal/dis/component/server/templating/templating.go @@ -2,7 +2,7 @@ package templating import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" + "github.com/tkw1536/pkglib/lazy" ) // Templating implements templating customization diff --git a/internal/dis/component/sql/sql.go b/internal/dis/component/sql/sql.go index 134d9b5..45554fa 100644 --- a/internal/dis/component/sql/sql.go +++ b/internal/dis/component/sql/sql.go @@ -7,7 +7,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/pkg/environment" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" + "github.com/tkw1536/pkglib/lazy" ) type SQL struct { diff --git a/internal/dis/distillery.go b/internal/dis/distillery.go index b644357..6fdeaa1 100644 --- a/internal/dis/distillery.go +++ b/internal/dis/distillery.go @@ -34,7 +34,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/ssh2/sshkeys" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/triplestore" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/web" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" + "github.com/tkw1536/pkglib/lifetime" ) // Distillery represents a WissKI Distillery @@ -54,9 +54,9 @@ type Distillery struct { // But for now this will just hold upstream configuration. Upstream Upstream - // pool holds all components - pool lazy.Pool[component.Component, component.Still] - poolInit sync.Once + // lifetime holds all components + lifetime lifetime.Lifetime[component.Component, component.Still] + lifetimeInit sync.Once } // Upstream contains the configuration for accessing remote configuration. @@ -186,7 +186,7 @@ func (dis *Distillery) allComponents() []initFunc { resolver.RefreshInterval = time.Minute }), manual(func(admin *admin.Admin) { - admin.Analytics = &dis.pool.Analytics + admin.Analytics = &dis.lifetime.Analytics }), auto[*socket.Sockets], auto[*legal.Legal], diff --git a/internal/phpx/server.go b/internal/phpx/server.go index 5c600bd..eb95236 100644 --- a/internal/phpx/server.go +++ b/internal/phpx/server.go @@ -11,10 +11,10 @@ import ( _ "embed" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/tkw1536/goprogram/stream" "github.com/tkw1536/pkglib/collection" "github.com/tkw1536/pkglib/contextx" + "github.com/tkw1536/pkglib/lazy" "github.com/tkw1536/pkglib/nobufio" ) diff --git a/internal/wisski/ingredient/info/info.go b/internal/wisski/ingredient/info/info.go index 9e99778..a1c3164 100644 --- a/internal/wisski/ingredient/info/info.go +++ b/internal/wisski/ingredient/info/info.go @@ -7,7 +7,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/status" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" + "github.com/tkw1536/pkglib/lifetime" "golang.org/x/sync/errgroup" ) @@ -18,7 +18,7 @@ type Info struct { Fetchers []ingredient.WissKIFetcher } - Analytics *lazy.PoolAnalytics + Analytics *lifetime.Analytics } var ( diff --git a/internal/wisski/ingredient/ingredient.go b/internal/wisski/ingredient/ingredient.go index e1ca97c..09242fb 100644 --- a/internal/wisski/ingredient/ingredient.go +++ b/internal/wisski/ingredient/ingredient.go @@ -9,7 +9,7 @@ import ( // Ingredients represent a part of a WissKI instance. // An Ingredient should be implemented as a pointer to a struct. -// Every ingredient must embed [Base] and should be initialized using [Init] inside a [lazy.Pool]. +// Every ingredient must embed [Base] and should be initialized using [Init] inside a [lifetime.Lifetime]. // // By convention these are defined within their corresponding subpackage. // This subpackage also contains all required resources. @@ -35,7 +35,7 @@ func (cb *Base) getBase() *Base { } // Init initializes a new Ingredient. -// Init is only intended to be used within a lazy.Pool[Ingredient,*Liquid]. +// Init is only intended to be used within a lifetime.Lifetime[Ingredient,*Liquid]. func Init(ingredient Ingredient, liquid *liquid.Liquid) Ingredient { base := ingredient.getBase() // pointer to a struct base.Liquid = liquid diff --git a/internal/wisski/ingredients.go b/internal/wisski/ingredients.go index ea87e0c..528a908 100644 --- a/internal/wisski/ingredients.go +++ b/internal/wisski/ingredients.go @@ -3,8 +3,8 @@ package wisski import ( "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient" "github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/tkw1536/pkglib/collection" + "github.com/tkw1536/pkglib/lifetime" ) // @@ -12,9 +12,9 @@ import ( // func (wisski *WissKI) init() { - wisski.poolInit.Do(func() { - wisski.pool.Init = ingredient.Init - lazy.RegisterPoolGroup[ingredient.WissKIFetcher](&wisski.pool) + wisski.lifetimeInit.Do(func() { + wisski.lifetime.Init = ingredient.Init + lifetime.RegisterGroup[ingredient.WissKIFetcher](&wisski.lifetime) }) } @@ -25,13 +25,13 @@ func (wisski *WissKI) init() { // manual initializes a component from the provided distillery. func manual[I ingredient.Ingredient](init func(ingredient I)) initFunc { return func(context ctx) ingredient.Ingredient { - return lazy.Make(context, init) + return lifetime.Make(context, init) } } // use is like r, but does not provided additional initialization func auto[I ingredient.Ingredient](context ctx) ingredient.Ingredient { - return lazy.Make[ingredient.Ingredient, I](context, nil) + return lifetime.Make[ingredient.Ingredient, I](context, nil) } // register returns all components of the distillery @@ -45,7 +45,7 @@ func (wisski *WissKI) register(context ctx) []ingredient.Ingredient { } // ctx is a context for component initialization -type ctx = *lazy.PoolContext[ingredient.Ingredient] +type ctx = *lifetime.InjectorContext[ingredient.Ingredient] // // ==== export ==== @@ -54,13 +54,13 @@ type ctx = *lazy.PoolContext[ingredient.Ingredient] // export is a convenience function to export a single component func export[I ingredient.Ingredient](wisski *WissKI) I { wisski.init() - return lazy.ExportComponent[ingredient.Ingredient, *liquid.Liquid, I](&wisski.pool, &wisski.Liquid, wisski.register) + return lifetime.ExportComponent[ingredient.Ingredient, *liquid.Liquid, I](&wisski.lifetime, &wisski.Liquid, wisski.register) } //lint:ignore U1000 for future use func exportAll[I ingredient.Ingredient](wisski *WissKI) []I { wisski.init() - return lazy.ExportComponents[ingredient.Ingredient, *liquid.Liquid, I](&wisski.pool, &wisski.Liquid, wisski.register) + return lifetime.ExportComponents[ingredient.Ingredient, *liquid.Liquid, I](&wisski.lifetime, &wisski.Liquid, wisski.register) } type initFunc = func(context ctx) ingredient.Ingredient diff --git a/internal/wisski/wisski.go b/internal/wisski/wisski.go index 3ad0bbe..42ba47b 100644 --- a/internal/wisski/wisski.go +++ b/internal/wisski/wisski.go @@ -18,7 +18,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/users" "github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/reserve" "github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" + "github.com/tkw1536/pkglib/lifetime" ) // WissKI represents a single WissKI Instance. @@ -26,8 +26,8 @@ import ( type WissKI struct { liquid.Liquid - poolInit sync.Once - pool lazy.Pool[ingredient.Ingredient, *liquid.Liquid] + lifetimeInit sync.Once + lifetime lifetime.Lifetime[ingredient.Ingredient, *liquid.Liquid] } // @@ -110,7 +110,7 @@ func (wisski *WissKI) allIngredients() []initFunc { // info manual(func(info *info.Info) { - info.Analytics = &wisski.pool.Analytics + info.Analytics = &wisski.lifetime.Analytics }), auto[*barrel.LastRebuildFetcher], auto[*barrel.RunningFetcher], diff --git a/pkg/httpx/socket.go b/pkg/httpx/socket.go index cba75a0..3ebea2b 100644 --- a/pkg/httpx/socket.go +++ b/pkg/httpx/socket.go @@ -6,8 +6,8 @@ import ( "sync" "time" - "github.com/FAU-CDI/wisski-distillery/pkg/lazy" "github.com/gorilla/websocket" + "github.com/tkw1536/pkglib/lazy" ) // WebSocket implements serving a WebSocket diff --git a/pkg/lazy/lazy.go b/pkg/lazy/lazy.go deleted file mode 100644 index 563c922..0000000 --- a/pkg/lazy/lazy.go +++ /dev/null @@ -1,50 +0,0 @@ -package lazy - -import ( - "sync" -) - -// Lazy holds a lazily initialized value of T. -// A non-zero lazy must not be copied after first use. -type Lazy[T any] struct { - once sync.Once - - m sync.RWMutex // m protects setting the value of this T - value T // the stored value -} - -// Get returns the value associated with this Lazy. -// -// If no other call to Get has started or completed an initialization, calls init to initialize the value. -// A nil init function indicates to store the zero value of T. -// If an initialization has been previously completed, the previously stored value is returned. -// -// If init panics, the initization is considered to be completed. -// Future calls to Get() do not invoke init, and the zero value of T is returned. -// -// Get may safely be called concurrently. -func (lazy *Lazy[T]) Get(init func() T) T { - lazy.m.RLock() - defer lazy.m.RUnlock() - - lazy.once.Do(func() { - if init != nil { - lazy.value = init() - } - }) - - return lazy.value -} - -// Set atomically sets the value of this lazy. -// Any previously set value will be overwritten. -// Future calls to [Get] will not invoke init. -// -// It may be called concurrently with calls to [Get]. -func (lazy *Lazy[T]) Set(value T) { - lazy.m.Lock() - defer lazy.m.Unlock() - - lazy.value = value - lazy.once.Do(func() {}) -} diff --git a/pkg/lazy/lazy_test.go b/pkg/lazy/lazy_test.go deleted file mode 100644 index 22e1ab1..0000000 --- a/pkg/lazy/lazy_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package lazy - -import "fmt" - -func ExampleLazy() { - - var lazy Lazy[int] - - // the first invocation to lazy will be called and set the value - fmt.Println(lazy.Get(func() int { return 42 })) - - // the second invocation will not call init again, using the first value - fmt.Println(lazy.Get(func() int { return 43 })) - - // Set can be used to set a specific value - lazy.Set(0) - fmt.Println(lazy.Get(func() int { panic("never called") })) - - // Output: 42 - // 42 - // 0 -} - -func ExampleLazy_nil() { - var lazy Lazy[int] - - // passing nil as the initialization function causes the zero value to be set - fmt.Println(lazy.Get(nil)) - - // Output: 0 -} diff --git a/pkg/lazy/pool.go b/pkg/lazy/pool.go deleted file mode 100644 index 934c630..0000000 --- a/pkg/lazy/pool.go +++ /dev/null @@ -1,189 +0,0 @@ -package lazy - -import ( - "reflect" - - "github.com/tkw1536/pkglib/reflectx" -) - -// Pool represents a pool of laziliy initialized and potentially referencing Component instances. -// -// Component must be an interface type, that should be implemented by various pointers to structs. -// Components may reference each other, even circularly. -// -// Each type of struct is considered a singleton an initialized only once. -// -// See [Pool.All], [ExportComponents] and [ExportComponent]. -// -// The zero value is ready to use. -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. - // - // Contains all groups and structs that are referenced during initialization. - // To add extra groups, call RegisterPoolGroup. - Analytics PoolAnalytics - extraGroups []reflect.Type - - all Lazy[[]Component] -} - -// RegisterPoolGroup registers the given group type to be added to the pools' analytics. -// -// Only groups not referenced during initialization need to be registered explicitly. -func RegisterPoolGroup[Group any, Component any, InitParams any](p *Pool[Component, InitParams]) { - p.extraGroups = append(p.extraGroups, reflectx.TypeOf[Group]()) -} - -// 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[Component, InitParams]) All(Params InitParams, All func(context *PoolContext[Component]) []Component) []Component { - return p.all.Get(func() []Component { - // create a new context - context := &PoolContext[Component]{ - all: All, - init: func(c Component) Component { - if p.Init == nil { - return c - } - return p.Init(c, Params) - }, - metaCache: make(map[reflect.Type]meta[Component]), - cache: make(map[string]Component), - } - - // and process them all - all := context.all(context) - context.process(all) - - // write out analytics - context.anal(&p.Analytics, p.extraGroups) - return all - }) -} - -// PoolContext is a context used during [Make]. -type PoolContext[Component any] struct { - init func(Component) Component // initializes a new component - all func(context *PoolContext[Component]) []Component // initializes all components - - metaCache map[reflect.Type]meta[Component] - cache map[string]Component // cached components - queue []delayedInit[Component] // init queue -} - -type delayedInit[Component any] struct { - meta meta[Component] - value reflect.Value -} - -// Process processes all components in the queue -func (p *PoolContext[Component]) process(all []Component) { - index := 0 - for len(p.queue) > index { - p.queue[index].Run(all) - index++ - } - p.queue = nil -} - -func (di *delayedInit[Component]) Run(all []Component) { - di.meta.InitComponent(di.value, all) -} - -// Make creates or returns a cached component of the given Context. -// -// Components are initialized by first -// 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 -// -// Such component-like fields are only initialized if one of the following conditions are met: -// -// - The field has a tag 'auto' with the value `true` -// - The field lives inside a struct field named `Dependencies` -// -// 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[Component any, ConcreteComponent any](context *PoolContext[Component], init func(component ConcreteComponent)) ConcreteComponent { - // get a description of the type - cd := getMeta[Component, ConcreteComponent](context.metaCache) - - // if an instance already exists, return it! - if instance, ok := context.cache[cd.Name]; ok { - return any(instance).(ConcreteComponent) - } - - // create a fresh new instance and store it in the cache - context.cache[cd.Name] = context.init(cd.New()) - instance := any(context.cache[cd.Name]).(ConcreteComponent) - - // call the passed init function - if init != nil { - init(instance) - } - - // and queue it up - if cd.NeedsInitComponent() { - context.queue = append(context.queue, delayedInit[Component]{ - meta: cd, - value: reflect.ValueOf(instance), - }) - } - - return instance -} - -// -// PUBLIC FUNCTIONS -// - -// ExportComponents exports all components that are a ConcreteComponentType from the pool. -// -// All should be the function of the core that initializes all components. -// All should only make calls to [InitComponent]. -func ExportComponents[Component any, InitParams any, ConcreteComponentType any]( - p *Pool[Component, InitParams], - Params InitParams, - All func(context *PoolContext[Component]) []Component, -) []ConcreteComponentType { - components := p.All(Params, All) - - results := make([]ConcreteComponentType, 0, len(components)) - for _, comp := range components { - if cc, ok := any(comp).(ConcreteComponentType); ok { - results = append(results, cc) - } - } - return results -} - -// ExportComponent exports the first component that is a ConcreteComponent from the pool. -// -// All should be the function of the core that initializes all components. -// All should only make calls to [InitComponent]. -func ExportComponent[Component any, InitParams any, ConcreteComponentType any]( - pool *Pool[Component, InitParams], - Params InitParams, - All func(context *PoolContext[Component]) []Component, -) ConcreteComponentType { - components := pool.All(Params, All) - - for _, comp := range components { - if cc, ok := any(comp).(ConcreteComponentType); ok { - return cc - } - } - - panic("ExportComponent: Attempted to export unregistered component") -} diff --git a/pkg/lazy/pool_anal.go b/pkg/lazy/pool_anal.go deleted file mode 100644 index 1383dbd..0000000 --- a/pkg/lazy/pool_anal.go +++ /dev/null @@ -1,112 +0,0 @@ -package lazy - -import ( - "reflect" - - "github.com/tkw1536/pkglib/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 - - DCFields map[string]string // fields of the auto field with type C for which C implements component - DIFields map[string]string // fields of the auto field []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, groups []reflect.Type) { - 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() - } - } - - // collect interfaces to analyze - ifaces := make([]reflect.Type, len(groups)) - copy(ifaces, groups) - - // 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].DCFields = collection.MapValues(meta.DCFields, 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 { - ifaces = append(ifaces, iface) - return nameOf(iface) - }) - anal.Components[meta.Name].DIFields = collection.MapValues(meta.DIFields, func(key string, iface reflect.Type) string { - ifaces = append(ifaces, iface) - return nameOf(iface) - }) - } - - // and analyze all ifaces - for _, iface := range ifaces { - name := nameOf(iface) - if _, ok := anal.Groups[name]; ok { - continue - } - - 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() - } - } - - for _, comp := range anal.Components { - slices.Sort(comp.Groups) - } -} diff --git a/pkg/lazy/pool_meta.go b/pkg/lazy/pool_meta.go deleted file mode 100644 index aa74c28..0000000 --- a/pkg/lazy/pool_meta.go +++ /dev/null @@ -1,177 +0,0 @@ -package lazy - -import ( - "reflect" - - "github.com/tkw1536/pkglib/collection" - "github.com/tkw1536/pkglib/reflectx" -) - -// getMeta gets the component belonging to a component type -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 := cache[tp]; ok { - return m - } - - // create a new m - var m meta[Component] - m.init(tp) - - // store it in the cache - cache[tp] = m - return m -} - -// meta stores meta-information about a specific component -type meta[Component any] struct { - Name string // the type name of this component - Elem reflect.Type // the element type of the component - - CFields map[string]reflect.Type // fields with type C for which C implements component - IFields map[string]reflect.Type // fields []I where I is an interface that implements component - - DCFields map[string]reflect.Type // fields with type C for which C inside auto field which implement component - DIFields map[string]reflect.Type // fields []I where I is an interface inside auto field that implements component -} - -// init initializes this meta -func (m *meta[Component]) init(tp reflect.Type) { - var component = reflectx.TypeOf[Component]() - - if tp.Kind() != reflect.Pointer && tp.Elem().Kind() != reflect.Struct { - panic("GetMeta: Type (" + tp.String() + ") must be backed by a pointer to slice") - } - - m.Elem = tp.Elem() - m.Name = nameOf(m.Elem) - - m.CFields = make(map[string]reflect.Type) - m.IFields = make(map[string]reflect.Type) - scanForFields(component, m.Name, m.Elem, false, m.CFields, m.IFields) - - // check if we have a dependencies field of struct type - dependenciesField, ok := m.Elem.FieldByName(dependencies) - if !ok { - return - } - - if dependenciesField.Type.Kind() != reflect.Struct { - panic("GetMeta: " + dependencies + " field (" + m.Name + ") is not a struct") - } - - // and initialize the type map of the given map - m.DCFields = make(map[string]reflect.Type) - m.DIFields = make(map[string]reflect.Type) - scanForFields(component, m.Name, dependenciesField.Type, true, m.DCFields, m.DIFields) -} - -// scanForFields scans the structtype for fields of component-like fields. -// they are then writen to the cFields and iFields maps. -// inDependenciesStruct indicates if we are inside a dependency struct -func scanForFields(component reflect.Type, elem string, structType reflect.Type, inDependenciesStruct bool, cFields map[string]reflect.Type, iFields map[string]reflect.Type) { - count := structType.NumField() - for i := 0; i < count; i++ { - field := structType.Field(i) - - if !inDependenciesStruct && field.Tag.Get("auto") != "true" { - continue - } - if inDependenciesStruct && field.Tag != "" { - panic("GetMeta: " + dependencies + " field (" + elem + ") contains field (" + field.Name + ") with tag") - } - - tp := field.Type - name := field.Name - - switch { - case implementsComponent(component, tp): - cFields[name] = tp - case implementsSlice(component, tp): - iFields[name] = tp.Elem() - case inDependenciesStruct: - panic("GetMeta: " + dependencies + " field (" + elem + ") contains non-auto fields") - } - } -} - -func implementsComponent(component reflect.Type, tp reflect.Type) bool { - return tp.Implements(component) && tp.Kind() == reflect.Pointer && tp.Elem().Kind() == reflect.Struct -} - -func implementsSlice(component reflect.Type, tp reflect.Type) bool { - return tp.Kind() == reflect.Slice && tp.Elem().Kind() == reflect.Interface && tp.Elem().Implements(component) -} - -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) -} - -// NeedsInitComponent -func (m meta[Component]) NeedsInitComponent() bool { - return len(m.CFields) > 0 || len(m.IFields) > 0 || len(m.DCFields) > 0 || len(m.DIFields) > 0 -} - -// name of the dependencies field -const dependencies = "Dependencies" - -// InitComponent sets up the fields of the given instance of a component. -func (m meta[Component]) InitComponent(instance reflect.Value, all []Component) { - elem := instance.Elem() - dependenciesElem := elem.FieldByName(dependencies) - - // assign the component fields - for field, eType := range m.CFields { - c := collection.First(all, func(c Component) bool { - return reflect.TypeOf(c).AssignableTo(eType) - }) - - field := elem.FieldByName(field) - field.Set(reflect.ValueOf(c)) - } - for field, eType := range m.DCFields { - c := collection.First(all, func(c Component) bool { - return reflect.TypeOf(c).AssignableTo(eType) - }) - - field := dependenciesElem.FieldByName(field) - field.Set(reflect.ValueOf(c)) - } - - // assign the interface subtypes - registryR := reflect.ValueOf(all) - for field, eType := range m.IFields { - cs := filterSubtype(registryR, eType) - field := elem.FieldByName(field) - field.Set(cs) - } - for field, eType := range m.DIFields { - cs := filterSubtype(registryR, eType) - field := dependenciesElem.FieldByName(field) - field.Set(cs) - } - -} - -// filterSubtype filters the slice of type []S into a slice of type []iface. -// S and I must be interface types. -func filterSubtype(sliceS reflect.Value, iface reflect.Type) reflect.Value { - len := sliceS.Len() - - // convert each element - result := reflect.MakeSlice(reflect.SliceOf(iface), 0, len) - for i := 0; i < len; i++ { - element := sliceS.Index(i) - if element.Elem().Type().Implements(iface) { - result = reflect.Append(result, element.Elem().Convert(iface)) - } - } - return result -}