Migrate pkg/lazy => pkglib/{lazy,lifetime}
This commit is contained in:
parent
c3ca8e2974
commit
5e89fadeeb
21 changed files with 54 additions and 612 deletions
|
|
@ -2,8 +2,8 @@ package dis
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"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/collection"
|
||||||
|
"github.com/tkw1536/pkglib/lifetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -11,18 +11,19 @@ import (
|
||||||
//
|
//
|
||||||
|
|
||||||
func (dis *Distillery) init() {
|
func (dis *Distillery) init() {
|
||||||
dis.poolInit.Do(func() {
|
dis.lifetimeInit.Do(func() {
|
||||||
dis.pool.Init = component.Init
|
dis.lifetime.Init = component.Init
|
||||||
lazy.RegisterPoolGroup[component.Backupable](&dis.pool)
|
|
||||||
lazy.RegisterPoolGroup[component.Snapshotable](&dis.pool)
|
lifetime.RegisterGroup[component.Backupable](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.DistilleryFetcher](&dis.pool)
|
lifetime.RegisterGroup[component.Snapshotable](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.Installable](&dis.pool)
|
lifetime.RegisterGroup[component.DistilleryFetcher](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.Provisionable](&dis.pool)
|
lifetime.RegisterGroup[component.Installable](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.Routeable](&dis.pool)
|
lifetime.RegisterGroup[component.Provisionable](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.Cronable](&dis.pool)
|
lifetime.RegisterGroup[component.Routeable](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.UserDeleteHook](&dis.pool)
|
lifetime.RegisterGroup[component.Cronable](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.Table](&dis.pool)
|
lifetime.RegisterGroup[component.UserDeleteHook](&dis.lifetime)
|
||||||
lazy.RegisterPoolGroup[component.Menuable](&dis.pool)
|
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.
|
// manual initializes a component from the provided distillery.
|
||||||
func manual[C component.Component](init func(component C)) initFunc {
|
func manual[C component.Component](init func(component C)) initFunc {
|
||||||
return func(context ctx) component.Component {
|
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
|
// use is like r, but does not provided additional initialization
|
||||||
func auto[C component.Component](context ctx) component.Component {
|
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
|
// 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
|
// ctx is a context for component initialization
|
||||||
type ctx = *lazy.PoolContext[component.Component]
|
type ctx = *lifetime.InjectorContext[component.Component]
|
||||||
|
|
||||||
//
|
//
|
||||||
// ==== export ====
|
// ==== export ====
|
||||||
|
|
@ -62,12 +63,12 @@ type ctx = *lazy.PoolContext[component.Component]
|
||||||
// export is a convenience function to export a single component
|
// export is a convenience function to export a single component
|
||||||
func export[C component.Component](dis *Distillery) C {
|
func export[C component.Component](dis *Distillery) C {
|
||||||
dis.init()
|
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 {
|
func exportAll[C component.Component](dis *Distillery) []C {
|
||||||
dis.init()
|
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
|
type initFunc = func(context ctx) component.Component
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,9 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"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/server/templating"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/tkw1536/pkglib/lazy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
// Components represents a logical subsystem of the distillery.
|
// Components represents a logical subsystem of the distillery.
|
||||||
// A Component should be implemented as a pointer to a struct.
|
// 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.
|
// By convention these are defined within their corresponding subpackage.
|
||||||
// This subpackage also contains all required resources.
|
// 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 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 {
|
func Init(component Component, core Still) Component {
|
||||||
base := component.getBase() // pointer to a struct
|
base := component.getBase() // pointer to a struct
|
||||||
base.Still = core
|
base.Still = core
|
||||||
|
|
|
||||||
|
|
@ -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/assets"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
"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/httpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
|
"github.com/tkw1536/pkglib/lazy"
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
"github.com/rs/zerolog"
|
"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/internal/dis/component/instances"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Admin struct {
|
type Admin struct {
|
||||||
|
|
@ -33,7 +33,7 @@ type Admin struct {
|
||||||
Sockets *socket.Sockets
|
Sockets *socket.Sockets
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics *lazy.PoolAnalytics
|
Analytics *lifetime.Analytics
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -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/assets"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/server/templating"
|
"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/httpx"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/tkw1536/pkglib/lifetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed "html/anal.html"
|
//go:embed "html/anal.html"
|
||||||
|
|
@ -27,7 +27,7 @@ var analTemplate = templating.Parse[analContext](
|
||||||
type analContext struct {
|
type analContext struct {
|
||||||
templating.RuntimeFlags
|
templating.RuntimeFlags
|
||||||
|
|
||||||
Analytics lazy.PoolAnalytics
|
Analytics lifetime.Analytics
|
||||||
}
|
}
|
||||||
|
|
||||||
func (admin *Admin) components(ctx context.Context) http.Handler {
|
func (admin *Admin) components(ctx context.Context) http.Handler {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
"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/dis/component/server/templating"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
"github.com/tkw1536/pkglib/lazy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Home struct {
|
type Home struct {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package templating
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"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
|
// Templating implements templating customization
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
"github.com/tkw1536/pkglib/lazy"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SQL struct {
|
type SQL struct {
|
||||||
|
|
|
||||||
|
|
@ -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/ssh2/sshkeys"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/triplestore"
|
"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/internal/dis/component/web"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
"github.com/tkw1536/pkglib/lifetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Distillery represents a WissKI Distillery
|
// Distillery represents a WissKI Distillery
|
||||||
|
|
@ -54,9 +54,9 @@ type Distillery struct {
|
||||||
// But for now this will just hold upstream configuration.
|
// But for now this will just hold upstream configuration.
|
||||||
Upstream Upstream
|
Upstream Upstream
|
||||||
|
|
||||||
// pool holds all components
|
// lifetime holds all components
|
||||||
pool lazy.Pool[component.Component, component.Still]
|
lifetime lifetime.Lifetime[component.Component, component.Still]
|
||||||
poolInit sync.Once
|
lifetimeInit sync.Once
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upstream contains the configuration for accessing remote configuration.
|
// Upstream contains the configuration for accessing remote configuration.
|
||||||
|
|
@ -186,7 +186,7 @@ func (dis *Distillery) allComponents() []initFunc {
|
||||||
resolver.RefreshInterval = time.Minute
|
resolver.RefreshInterval = time.Minute
|
||||||
}),
|
}),
|
||||||
manual(func(admin *admin.Admin) {
|
manual(func(admin *admin.Admin) {
|
||||||
admin.Analytics = &dis.pool.Analytics
|
admin.Analytics = &dis.lifetime.Analytics
|
||||||
}),
|
}),
|
||||||
auto[*socket.Sockets],
|
auto[*socket.Sockets],
|
||||||
auto[*legal.Legal],
|
auto[*legal.Legal],
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@ import (
|
||||||
|
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
||||||
"github.com/tkw1536/goprogram/stream"
|
"github.com/tkw1536/goprogram/stream"
|
||||||
"github.com/tkw1536/pkglib/collection"
|
"github.com/tkw1536/pkglib/collection"
|
||||||
"github.com/tkw1536/pkglib/contextx"
|
"github.com/tkw1536/pkglib/contextx"
|
||||||
|
"github.com/tkw1536/pkglib/lazy"
|
||||||
"github.com/tkw1536/pkglib/nobufio"
|
"github.com/tkw1536/pkglib/nobufio"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/status"
|
"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"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php"
|
"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"
|
"golang.org/x/sync/errgroup"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -18,7 +18,7 @@ type Info struct {
|
||||||
Fetchers []ingredient.WissKIFetcher
|
Fetchers []ingredient.WissKIFetcher
|
||||||
}
|
}
|
||||||
|
|
||||||
Analytics *lazy.PoolAnalytics
|
Analytics *lifetime.Analytics
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
// Ingredients represent a part of a WissKI instance.
|
// Ingredients represent a part of a WissKI instance.
|
||||||
// An Ingredient should be implemented as a pointer to a struct.
|
// 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.
|
// By convention these are defined within their corresponding subpackage.
|
||||||
// This subpackage also contains all required resources.
|
// This subpackage also contains all required resources.
|
||||||
|
|
@ -35,7 +35,7 @@ func (cb *Base) getBase() *Base {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes a new Ingredient.
|
// 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 {
|
func Init(ingredient Ingredient, liquid *liquid.Liquid) Ingredient {
|
||||||
base := ingredient.getBase() // pointer to a struct
|
base := ingredient.getBase() // pointer to a struct
|
||||||
base.Liquid = liquid
|
base.Liquid = liquid
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ package wisski
|
||||||
import (
|
import (
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/liquid"
|
"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/collection"
|
||||||
|
"github.com/tkw1536/pkglib/lifetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -12,9 +12,9 @@ import (
|
||||||
//
|
//
|
||||||
|
|
||||||
func (wisski *WissKI) init() {
|
func (wisski *WissKI) init() {
|
||||||
wisski.poolInit.Do(func() {
|
wisski.lifetimeInit.Do(func() {
|
||||||
wisski.pool.Init = ingredient.Init
|
wisski.lifetime.Init = ingredient.Init
|
||||||
lazy.RegisterPoolGroup[ingredient.WissKIFetcher](&wisski.pool)
|
lifetime.RegisterGroup[ingredient.WissKIFetcher](&wisski.lifetime)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -25,13 +25,13 @@ func (wisski *WissKI) init() {
|
||||||
// manual initializes a component from the provided distillery.
|
// manual initializes a component from the provided distillery.
|
||||||
func manual[I ingredient.Ingredient](init func(ingredient I)) initFunc {
|
func manual[I ingredient.Ingredient](init func(ingredient I)) initFunc {
|
||||||
return func(context ctx) ingredient.Ingredient {
|
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
|
// use is like r, but does not provided additional initialization
|
||||||
func auto[I ingredient.Ingredient](context ctx) ingredient.Ingredient {
|
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
|
// 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
|
// ctx is a context for component initialization
|
||||||
type ctx = *lazy.PoolContext[ingredient.Ingredient]
|
type ctx = *lifetime.InjectorContext[ingredient.Ingredient]
|
||||||
|
|
||||||
//
|
//
|
||||||
// ==== export ====
|
// ==== export ====
|
||||||
|
|
@ -54,13 +54,13 @@ type ctx = *lazy.PoolContext[ingredient.Ingredient]
|
||||||
// export is a convenience function to export a single component
|
// export is a convenience function to export a single component
|
||||||
func export[I ingredient.Ingredient](wisski *WissKI) I {
|
func export[I ingredient.Ingredient](wisski *WissKI) I {
|
||||||
wisski.init()
|
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
|
//lint:ignore U1000 for future use
|
||||||
func exportAll[I ingredient.Ingredient](wisski *WissKI) []I {
|
func exportAll[I ingredient.Ingredient](wisski *WissKI) []I {
|
||||||
wisski.init()
|
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
|
type initFunc = func(context ctx) ingredient.Ingredient
|
||||||
|
|
|
||||||
|
|
@ -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/php/users"
|
||||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/reserve"
|
"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/internal/wisski/liquid"
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
"github.com/tkw1536/pkglib/lifetime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WissKI represents a single WissKI Instance.
|
// WissKI represents a single WissKI Instance.
|
||||||
|
|
@ -26,8 +26,8 @@ import (
|
||||||
type WissKI struct {
|
type WissKI struct {
|
||||||
liquid.Liquid
|
liquid.Liquid
|
||||||
|
|
||||||
poolInit sync.Once
|
lifetimeInit sync.Once
|
||||||
pool lazy.Pool[ingredient.Ingredient, *liquid.Liquid]
|
lifetime lifetime.Lifetime[ingredient.Ingredient, *liquid.Liquid]
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -110,7 +110,7 @@ func (wisski *WissKI) allIngredients() []initFunc {
|
||||||
|
|
||||||
// info
|
// info
|
||||||
manual(func(info *info.Info) {
|
manual(func(info *info.Info) {
|
||||||
info.Analytics = &wisski.pool.Analytics
|
info.Analytics = &wisski.lifetime.Analytics
|
||||||
}),
|
}),
|
||||||
auto[*barrel.LastRebuildFetcher],
|
auto[*barrel.LastRebuildFetcher],
|
||||||
auto[*barrel.RunningFetcher],
|
auto[*barrel.RunningFetcher],
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/tkw1536/pkglib/lazy"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WebSocket implements serving a WebSocket
|
// WebSocket implements serving a WebSocket
|
||||||
|
|
|
||||||
|
|
@ -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() {})
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
189
pkg/lazy/pool.go
189
pkg/lazy/pool.go
|
|
@ -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")
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue