diff --git a/app.go b/app.go deleted file mode 100644 index 23080fd..0000000 --- a/app.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package app provides the core setup and teardown functions for the -// backend application. It handles the initialization of all sub-systems -// and the logger. app should never be imported except by a main package. -// Examples of suitable main packages are cmd and test. -package app diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..e31623f --- /dev/null +++ b/errors.go @@ -0,0 +1,6 @@ +package app + +import "errors" + +// ErrModuleNotFound is returned when a module is not found in the lifecycle. +var ErrModuleNotFound = errors.New("module not found") diff --git a/module.go b/module.go index 0a70322..08127c3 100644 --- a/module.go +++ b/module.go @@ -1,25 +1,14 @@ package app import ( - "errors" "fmt" "log/slog" "strings" ) -// ErrModuleNotFound is returned when a module is not found in the lifecycle. -var ErrModuleNotFound = errors.New("module not found") - // ModuleFn is a function type for module setup and teardown functions. type ModuleFn func(*Module) error -// GenericModule is an interface that allows modules to be extended with custom -// functionality, as the module can return a pointer to the underlying Module. -type GenericModule interface { - // Module returns the underlying Module instance. - Module() *Module -} - // Module represents a sub-system of the application, with its setup and // teardown functions and dependencies. type Module struct { @@ -60,7 +49,8 @@ func (s *Module) Lifecycle() *Lifecycle { } // Logger returns the logger for the module. Uses the lifecycle's logger unless -// a specific logger has been set during module load. +// a specific logger has been set during module load. Panics if the module is +// not associated with a lifecycle and no override logger was set. func (s *Module) Logger() *slog.Logger { if s.logger != nil { return s.logger diff --git a/require.go b/require.go new file mode 100644 index 0000000..1038a8e --- /dev/null +++ b/require.go @@ -0,0 +1,61 @@ +package app + +import ( + "fmt" + "log/slog" +) + +// RequireOpts configures [Lifecycle.RequireWithOpts] behavior. +type RequireOpts struct { + // Logger overrides the lifecycle logger for the required modules. If nil, + // the lifecycle logger is used. + Logger *slog.Logger + + // Unique, when true, returns an error if any requested module name is + // already set up instead of ignoring duplicate instances. + Unique bool +} + +// Require adds module(s) to the lifecycle and immediately runs setup. +// Modules that are already set up (by name) are ignored. +func (app *Lifecycle) Require(modules ...*Module) error { + return app.require(RequireOpts{}, modules...) +} + +// RequireWithOpts is like [Lifecycle.Require] with additional options. +func (app *Lifecycle) RequireWithOpts(opts RequireOpts, modules ...*Module) error { + return app.require(opts, modules...) +} + +// GetModule returns a registered module by name. +func (app *Lifecycle) GetModule(name string) (*Module, error) { + mod, err := app.getModuleByName(name) + if err != nil { + return nil, fmt.Errorf("get module %q: %w", name, err) + } + return mod, nil +} + +// RequireL loads modules using a specific logger. Deprecated: use +// RequireWithOpts(RequireOpts{Logger: logger}, modules...). +func (app *Lifecycle) RequireL(logger *slog.Logger, modules ...*Module) error { + if logger == nil { + return fmt.Errorf("logger cannot be nil") + } + return app.require(RequireOpts{Logger: logger}, modules...) +} + +// RequireUnique loads modules and errors if any name is already set up. +// Deprecated: use RequireWithOpts(RequireOpts{Unique: true}, modules...). +func (app *Lifecycle) RequireUnique(modules ...*Module) error { + return app.require(RequireOpts{Unique: true}, modules...) +} + +// RequireUniqueL combines RequireL and RequireUnique. +// Deprecated: use RequireWithOpts(RequireOpts{Logger: logger, Unique: true}, modules...). +func (app *Lifecycle) RequireUniqueL(logger *slog.Logger, modules ...*Module) error { + if logger == nil { + return fmt.Errorf("logger cannot be nil") + } + return app.require(RequireOpts{Logger: logger, Unique: true}, modules...) +}