feat(module): add four-phase Module interface and registry

This commit is contained in:
MDietermann
2026-04-13 19:29:18 +02:00
parent 5b5e677749
commit eb54225ad3
3 changed files with 174 additions and 0 deletions

61
internal/module/module.go Normal file
View File

@@ -0,0 +1,61 @@
// Package module defines the four-phase contract every installable unit
// implements, along with the value types exchanged with the engine.
package module
import "context"
// Meta is static, declarative information about a module.
type Meta struct {
ID string // stable machine identifier, e.g. "packages"
Name string // human label, e.g. "Base packages"
Description string // one-line summary for TUI
Deps []string // IDs of modules that must complete before this one
}
// State is an opaque, module-defined snapshot of current system state.
// The engine never inspects it; it is threaded from Detect → Plan.
type State any
// ActionKind categorizes what an Action does, for UI grouping only.
type ActionKind int
const (
ActionUnknown ActionKind = iota
ActionPackageInstall
ActionFileWrite
ActionSymlink
ActionCommand
ActionGitClone
ActionGroupAdd
)
// Action is a single planned change with a human-readable description.
type Action struct {
Description string
Kind ActionKind
Exec func(ctx context.Context) error
}
// Plan is an ordered list of actions returned by Module.Plan.
type Plan struct {
Actions []Action
}
// IsEmpty reports whether the plan would make no changes.
func (p Plan) IsEmpty() bool { return len(p.Actions) == 0 }
// Module is the four-phase contract.
type Module interface {
Meta() Meta
Detect(ctx context.Context) (State, error)
Plan(ctx context.Context, current State) (Plan, error)
Apply(ctx context.Context, plan Plan, r Reporter) error
Verify(ctx context.Context) error
}
// Reporter is how modules stream progress events. Defined here as an
// interface to avoid an import cycle with the engine package.
type Reporter interface {
Progress(moduleID string, current, total int, message string)
Log(moduleID, line string)
}