105 lines
2.8 KiB
Go
105 lines
2.8 KiB
Go
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 {
|
|
lifecycle *Lifecycle // lifecycle is the parent lifecycle this module belongs to
|
|
logger *slog.Logger
|
|
name string
|
|
setup moduleFn
|
|
teardown moduleFn
|
|
depends []string
|
|
loaded bool // loaded indicates if the module has been set up
|
|
}
|
|
|
|
// ModuleOpts contains user-exposed options when defining a module.
|
|
type ModuleOpts struct {
|
|
// Setup is the setup function for the module.
|
|
Setup moduleFn
|
|
// Teardown is the teardown function for the module.
|
|
Teardown moduleFn
|
|
// Depends is a list of modules that this module depends on. Each entry must
|
|
// be the exact name of a module registered in the lifecycle.
|
|
Depends []string
|
|
}
|
|
|
|
// NewModule creates a new Module instance with the given name and options.
|
|
func NewModule(name string, opts ModuleOpts) *Module {
|
|
return &Module{
|
|
name: name,
|
|
setup: opts.Setup,
|
|
teardown: opts.Teardown,
|
|
depends: opts.Depends,
|
|
}
|
|
}
|
|
|
|
// Lifecycle returns the lifecycle this module belongs to. If the module is not
|
|
// part of a lifecycle, it will return nil.
|
|
func (s *Module) Lifecycle() *Lifecycle {
|
|
return s.lifecycle
|
|
}
|
|
|
|
// Logger returns the logger for the module. Uses the lifecycle's logger unless
|
|
// a specific logger has been set during module load.
|
|
func (s *Module) Logger() *slog.Logger {
|
|
if s.logger != nil {
|
|
return s.logger
|
|
}
|
|
if s.lifecycle == nil {
|
|
panic("module has no lifecycle, cannot get logger")
|
|
}
|
|
return s.lifecycle.Logger().With("module", s.name)
|
|
}
|
|
|
|
// Name returns the name of the module.
|
|
func (s *Module) Name() string {
|
|
return s.name
|
|
}
|
|
|
|
// String returns the name of the module in square brackets, e.g. "[module-name]".
|
|
func (s *Module) String() string {
|
|
return fmt.Sprintf("[%s]", s.name)
|
|
}
|
|
|
|
// Loaded returns whether the module has been set up.
|
|
func (s *Module) Loaded() bool {
|
|
return s.loaded
|
|
}
|
|
|
|
// RequireLoaded panics if the module is not loaded. Any arguments are included
|
|
// in the panic message for additional context.
|
|
func (s *Module) RequireLoaded(msg ...string) {
|
|
ctx := ""
|
|
if len(msg) > 0 {
|
|
ctx = ": " + strings.Join(msg, ", ")
|
|
}
|
|
|
|
if s == nil {
|
|
panic("module is nil" + ctx)
|
|
}
|
|
|
|
if !s.loaded {
|
|
panic(fmt.Sprintf("module %s not loaded", s.name) + ctx)
|
|
}
|
|
}
|