package app import ( "errors" "fmt" "log/slog" ) // 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 // Module represents a sub-system of the application, with its setup and // teardown functions and dependencies. type Module struct { 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, } } // Logger returns the logger for the module. func (s *Module) Logger() *slog.Logger { if s.logger == nil { panic(fmt.Sprintf("module %s used before logger was initialized", s.name)) } return s.logger } // Name returns the name of the module. func (s *Module) Name() string { return 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. func (s *Module) RequireLoaded() { if s == nil || !s.Loaded() { panic(fmt.Sprintf("module %s not loaded", s.name)) } }