add Lifecycle.RequireUnique methods for load-once deps
This commit is contained in:
23
lifecycle.go
23
lifecycle.go
@@ -123,6 +123,12 @@ func (app *Lifecycle) Require(modules ...*Module) error {
|
|||||||
return app.RequireL(app.logger, modules...)
|
return app.RequireL(app.logger, modules...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequireUnique is the same as Require, but it returns an error if any requested
|
||||||
|
// module is already set up--rather than ignoring it.
|
||||||
|
func (app *Lifecycle) RequireUnique(modules ...*Module) error {
|
||||||
|
return app.RequireUniqueL(app.logger, modules...)
|
||||||
|
}
|
||||||
|
|
||||||
// RequireL adds module(s) to the lifecycle with a specific logger and
|
// RequireL adds module(s) to the lifecycle with a specific logger and
|
||||||
// immediately runs any setup functions. See Require for more details.
|
// immediately runs any setup functions. See Require for more details.
|
||||||
// This variation is useful when you need to set up modules with a non-
|
// This variation is useful when you need to set up modules with a non-
|
||||||
@@ -163,6 +169,23 @@ func (app *Lifecycle) RequireL(logger *slog.Logger, modules ...*Module) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequireUniqueL is the same as RequireL, but it returns an error if any requested
|
||||||
|
// module is already set up--rather than ignoring it.
|
||||||
|
func (app *Lifecycle) RequireUniqueL(logger *slog.Logger, modules ...*Module) error {
|
||||||
|
// Check if any module is already set up
|
||||||
|
for _, mod := range modules {
|
||||||
|
if mod == nil {
|
||||||
|
return fmt.Errorf("module is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := app.setupTracker[mod.name]; ok {
|
||||||
|
return fmt.Errorf("module '%s' is already set up, cannot require it again", mod.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.RequireL(logger, modules...)
|
||||||
|
}
|
||||||
|
|
||||||
// setupSingle is a helper function to set up a single module. Returns an error
|
// setupSingle is a helper function to set up a single module. Returns an error
|
||||||
// if the module cannot be set up or if dependencies are not satisfied.
|
// if the module cannot be set up or if dependencies are not satisfied.
|
||||||
func (app *Lifecycle) setupSingle(mod *Module, logger *slog.Logger) error {
|
func (app *Lifecycle) setupSingle(mod *Module, logger *slog.Logger) error {
|
||||||
|
|||||||
@@ -222,6 +222,14 @@ func TestLifecycle_Require(t *testing.T) {
|
|||||||
assert.Equal(t, lc.logger, mod.logger, "expected module logger to match lifecycle logger")
|
assert.Equal(t, lc.logger, mod.logger, "expected module logger to match lifecycle logger")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLifecycle_RequireUnique(t *testing.T) {
|
||||||
|
lc := NewLifecycle()
|
||||||
|
mod := NewModule("module1", ModuleOpts{})
|
||||||
|
err := lc.RequireUnique(mod)
|
||||||
|
assert.NoError(t, err, "expected RequireL to succeed")
|
||||||
|
assert.Equal(t, lc.logger, mod.logger, "expected module logger to match lifecycle logger")
|
||||||
|
}
|
||||||
|
|
||||||
func TestLifecycle_RequireL(t *testing.T) {
|
func TestLifecycle_RequireL(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -324,7 +332,56 @@ func TestLifecycle_RequireL(t *testing.T) {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLifecycle_RequireUniqueL(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
modules []*Module
|
||||||
|
existingModules []string
|
||||||
|
expectedErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no existing modules",
|
||||||
|
modules: []*Module{
|
||||||
|
NewModule("module1", ModuleOpts{}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "module already set up",
|
||||||
|
modules: []*Module{
|
||||||
|
NewModule("module1", ModuleOpts{}),
|
||||||
|
NewModule("module2", ModuleOpts{}),
|
||||||
|
},
|
||||||
|
existingModules: []string{"module1"},
|
||||||
|
expectedErr: "module 'module1' is already set up, cannot require it again",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nil module",
|
||||||
|
modules: []*Module{nil},
|
||||||
|
expectedErr: "module is nil",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
lc := NewLifecycle()
|
||||||
|
|
||||||
|
// Fake existing modules
|
||||||
|
for _, modName := range tc.existingModules {
|
||||||
|
lc.setupTracker[modName] = 0 // Mark as set up
|
||||||
|
}
|
||||||
|
|
||||||
|
err := lc.RequireUniqueL(lc.logger, tc.modules...)
|
||||||
|
if tc.expectedErr == "" {
|
||||||
|
assert.NoError(err, "expected RequireUniqueL to succeed")
|
||||||
|
} else {
|
||||||
|
assert.Error(err, "expected RequireUniqueL to fail")
|
||||||
|
assert.Contains(err.Error(), tc.expectedErr, "expected error message to match")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLifecycle_setupSingle(t *testing.T) {
|
func TestLifecycle_setupSingle(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user