Documentation
¶
Overview ¶
Package loom is a compile-time dependency injection toolkit for Go.
Dependency graphs are declared with Graph and ordinary constructor functions; the `loom` command reads those declarations and generates plain Go initialization code. Nothing about graph resolution happens at runtime: the generated code calls constructors directly.
The only runtime primitive shipped by this package is Lifecycle, an ordered list of start/stop hooks plus constructor cleanups. It exists so that generated applications can start and stop long-lived services in the right order. It is not a container and performs no dependency lookup.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrAlreadyStarted is returned by Start when the lifecycle is already // running. ErrAlreadyStarted = errors.New("loom: lifecycle is already started") // ErrAlreadyStopped is returned by Start when the lifecycle has been // stopped. Lifecycles are single-use and cannot be restarted. ErrAlreadyStopped = errors.New("loom: lifecycle is already stopped") // ErrStartFailed is returned by Start when a previous Start attempt failed. ErrStartFailed = errors.New("loom: lifecycle failed to start") )
Sentinel errors returned by Lifecycle.Start.
Functions ¶
This section is empty.
Types ¶
type Cleanup ¶
Cleanup releases a resource acquired while a value was constructed.
A provider may return a Cleanup alongside its value:
func NewDB(cfg *Config) (*DB, loom.Cleanup, error)
Generated code registers each cleanup with the Lifecycle as soon as the constructor returns. Cleanups run in reverse registration order: during Lifecycle.Rollback when construction fails, or as part of Lifecycle.Stop on normal shutdown.
type GraphDef ¶
type GraphDef[T any] struct { // contains filtered or unexported fields }
GraphDef is the value produced by a Graph declaration. It is a compile-time marker: the generated initializer does not reference it.
func Graph ¶
Graph declares the dependency graph used to build T.
// AppGraph wires the application. var AppGraph = loom.Graph[*App]( loom.Provide(NewConfig), loom.Provide(NewDB), loom.Provide(NewApp), )
Graph must be assigned to a package-level variable. The generated initializer is named after that variable: AppGraph produces InitApp. Use Name to override the name.
type Hook ¶
type Hook struct {
// OnStart runs when the lifecycle starts. A nil OnStart is a no-op.
OnStart func(context.Context) error
// OnStop runs when the lifecycle stops. A nil OnStop is a no-op.
OnStop func(context.Context) error
}
Hook couples an optional start callback with an optional stop callback.
Hooks registered with Lifecycle.Append run in registration order on Lifecycle.Start and in reverse registration order on Lifecycle.Stop.
type Lifecycle ¶
type Lifecycle struct {
// contains filtered or unexported fields
}
Lifecycle is the runtime primitive used by generated code to start and stop an application in dependency order.
The zero value is not ready for use; construct one with NewLifecycle. Lifecycle is safe for concurrent use by multiple goroutines.
func NewLifecycle ¶
func NewLifecycle() *Lifecycle
NewLifecycle returns an empty, initialized lifecycle.
func (*Lifecycle) AddCleanup ¶
AddCleanup registers a constructor cleanup. Cleanups run in reverse registration order, after any stop hooks that were started.
AddCleanup must be called before Start. Calling it once Start has begun panics. A nil cleanup is ignored, which lets generated code register a constructor's cleanup unconditionally.
func (*Lifecycle) Append ¶
Append registers a hook. Hooks are started in registration order and stopped in reverse registration order.
Append must be called before Start. Calling it once Start has begun panics, because a hook registered concurrently with startup cannot reliably receive its OnStart call.
func (*Lifecycle) Rollback ¶
Rollback releases resources acquired during a failed construction. It runs only registered cleanups, in reverse registration order, and never runs OnStop hooks (nothing was started). Generated code calls Rollback when a constructor returns an error.
Rollback ignores context cancellation so that a failed shutdown does not abandon still-open resources. It is a no-op unless the lifecycle is still initialized, so a later Stop cannot release the same resources twice.
func (*Lifecycle) Start ¶
Start runs every registered OnStart hook in registration order.
If a hook fails or ctx is canceled, Start rolls back: it runs Stop hooks and cleanups for everything started so far, in reverse registration order, and returns the start error joined with any rollback errors. A lifecycle whose start failed cannot be started again.
func (*Lifecycle) State ¶
State reports the current lifecycle state. It is intended for diagnostics and tests.
func (*Lifecycle) Stop ¶
Stop runs registered OnStop hooks and cleanups in reverse registration order. Hooks are stopped only if their OnStart ran successfully; cleanups always run.
Errors from individual hooks are aggregated with errors.Join so that one failing hook does not prevent the rest of the shutdown. If ctx is canceled mid-shutdown, Stop records ctx.Err() and skips remaining stop hooks, but still runs constructor cleanups with cancellation detached to avoid leaks. Stop is idempotent: calling it again after the lifecycle has stopped returns nil.
type ModuleDef ¶
type ModuleDef struct {
// contains filtered or unexported fields
}
ModuleDef is the value produced by a Module declaration. Like GraphDef it is a compile-time marker that generated code does not reference.
type Option ¶
type Option interface {
// contains filtered or unexported methods
}
Option is a compile-time marker accepted by Graph and Module.
Option is a sealed interface: only this package can implement it, so graph declarations cannot be extended with arbitrary values.
func Name ¶
Name sets the name of the function generated for a graph.
var appGraph = loom.Graph[*App](loom.Name("BuildApp"), ...)
func WithContext ¶
func WithContext() Option
WithContext makes the generated initializer accept a context.Context and makes context.Context an injectable dependency.
func InitApp(ctx context.Context) (*App, *loom.Lifecycle, error)
Prefer doing I/O in a lifecycle OnStart hook; use WithContext when a constructor genuinely needs a context, such as a database dialer.
type Provider ¶
type Provider struct {
// contains filtered or unexported fields
}
Provider marks a constructor as a provider. Construct one with Provide, As, or Supply; the zero value is meaningless.
func As ¶
As registers ctor as a provider of the interface I.
loom.As[UserStore](NewUserStore)
The generator verifies that the constructor's result implements I and reports a diagnostic at the provider's source location if it does not. The constructor's concrete result type is provided as well, so a binding never duplicates an instance.
func Provide ¶
Provide registers ctor as a provider of the value it returns.
ctor must be a package-level function (or a package-level variable holding a function) matching one of:
func(...) T func(...) (T, error) func(...) (T, Cleanup) func(...) (T, Cleanup, error)
Generic constructors must be instantiated explicitly:
loom.Provide(NewRepository[User])
func Supply ¶
Supply registers an existing value as a provider.
loom.Supply(config)
The expression must be reproducible in generated code: identifiers, package qualified names, field selections, literals, composite literals, and calls whose operands are themselves reproducible. Note that Go evaluates the expression once during package initialization and again inside the generated initializer; assign side-effecting expressions to a package-level variable first.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
loom
command
Command loom generates dependency-injection code for Go programs.
|
Command loom generates dependency-injection code for Go programs. |
|
examples
|
|
|
full
Package full is the end-to-end loom example: generics, an interface binding, modules, supply, lifecycle hooks, and constructor cleanups in one graph.
|
Package full is the end-to-end loom example: generics, an interface binding, modules, supply, lifecycle hooks, and constructor cleanups in one graph. |
|
internal
|
|
|
codegen
Package codegen renders a resolved plan into gofmt-compatible Go source.
|
Package codegen renders a resolved plan into gofmt-compatible Go source. |
|
diag
Package diag renders human-friendly diagnostics for graph problems.
|
Package diag renders human-friendly diagnostics for graph problems. |
|
gen
Package gen wires loading, parsing, resolving, and code generation into a single operation.
|
Package gen wires loading, parsing, resolving, and code generation into a single operation. |
|
load
Package load wraps go/packages so that the rest of Loom works with a single, consistent go/types universe.
|
Package load wraps go/packages so that the rest of Loom works with a single, consistent go/types universe. |
|
model
Package model holds Loom's intermediate representation: providers and graphs expressed in terms of go/types, never in terms of strings or AST nodes alone.
|
Package model holds Loom's intermediate representation: providers and graphs expressed in terms of go/types, never in terms of strings or AST nodes alone. |
|
parse
Package parse discovers loom.Graph declarations and turns them into the model IR.
|
Package parse discovers loom.Graph declarations and turns them into the model IR. |
|
resolve
Package resolve turns a parsed graph into a topologically ordered build plan.
|
Package resolve turns a parsed graph into a topologically ordered build plan. |