diff --git a/internal/directives/copy_directive.go b/internal/directives/copy_directive.go index 59baaa238..91028a21c 100644 --- a/internal/directives/copy_directive.go +++ b/internal/directives/copy_directive.go @@ -8,7 +8,7 @@ import ( func init() { // Register the copy directive with the builtins registry. - builtins.RegisterStep(©Directive{}) + builtins.RegisterDirective(©Directive{}) } // copyDirective is a directive that copies a file or directory. diff --git a/internal/directives/step.go b/internal/directives/directive.go similarity index 78% rename from internal/directives/step.go rename to internal/directives/directive.go index 2dc9a116d..4b6bdf53c 100644 --- a/internal/directives/step.go +++ b/internal/directives/directive.go @@ -53,24 +53,24 @@ func (c Config) DeepCopy() Config { return runtime.DeepCopyJSON(c) } -// Result is a type that represents the result of a step. +// Result is a type that represents the result of a Directive. type Result string const ( - // ResultSuccess is the result of a successful step. + // ResultSuccess is the result of a successful directive. ResultSuccess Result = "Success" - // ResultFailure is the result of a failed step. + // ResultFailure is the result of a failed directive. ResultFailure Result = "Failure" ) -// Step is an interface that represents a single step in a list of directives -// that should be executed in sequence. Each step is responsible for executing -// a specific action, and may modify the provided context to allow subsequent -// steps to access the results of its execution. -type Step interface { - // Name returns the name of the step. +// Directive is an interface that a directive must implement. A directive is +// a responsible for executing a specific action, and may modify the provided +// context to allow subsequent directives to access the results of its +// execution. +type Directive interface { + // Name returns the name of the directive. Name() string - // Run executes the step using the provided context and configuration. + // Run executes the directive using the provided context and configuration. Run(ctx context.Context, stepCtx *StepContext) (Result, error) } diff --git a/internal/directives/step_test.go b/internal/directives/directive_test.go similarity index 100% rename from internal/directives/step_test.go rename to internal/directives/directive_test.go diff --git a/internal/directives/engine.go b/internal/directives/engine.go index 602064b00..2834088ed 100644 --- a/internal/directives/engine.go +++ b/internal/directives/engine.go @@ -6,10 +6,10 @@ import ( "os" ) -// Directive is a single directive that should be executed by the Engine. -type Directive struct { - // Step is the name of the step to execute. - Step string +// Step is a single step that should be executed by the Engine. +type Step struct { + // Directive is the name of the directive to execute for this step. + Directive string // Alias is an optional alias for the step, which can be used to // refer to its results in subsequent steps. Alias string @@ -19,20 +19,20 @@ type Directive struct { // Engine is a simple engine that executes a list of directives in sequence. type Engine struct { - registry StepRegistry + registry DirectiveRegistry } -// NewEngine returns a new Engine with the provided StepRegistry. -func NewEngine(registry StepRegistry) *Engine { +// NewEngine returns a new Engine with the provided DirectiveRegistry. +func NewEngine(registry DirectiveRegistry) *Engine { return &Engine{ registry: registry, } } // Execute runs the provided list of directives in sequence. -func (e *Engine) Execute(ctx context.Context, directives []Directive) (Result, error) { +func (e *Engine) Execute(ctx context.Context, steps []Step) (Result, error) { // TODO(hidde): allow the workDir to be restored from a previous execution. - workDir, err := os.CreateTemp("", "directives-") + workDir, err := os.CreateTemp("", "run-") if err != nil { return ResultFailure, fmt.Errorf("temporary working directory creation failed: %w", err) } @@ -41,14 +41,14 @@ func (e *Engine) Execute(ctx context.Context, directives []Directive) (Result, e // Initialize the shared state that will be passed to each step. state := make(State) - for _, d := range directives { + for _, d := range steps { select { case <-ctx.Done(): return ResultFailure, ctx.Err() default: - step, err := e.registry.GetStep(d.Step) + step, err := e.registry.GetDirective(d.Directive) if err != nil { - return ResultFailure, fmt.Errorf("failed to get step %q: %w", d.Step, err) + return ResultFailure, fmt.Errorf("failed to get step %q: %w", d.Directive, err) } if result, err := step.Run(ctx, &StepContext{ @@ -57,7 +57,7 @@ func (e *Engine) Execute(ctx context.Context, directives []Directive) (Result, e Alias: d.Alias, Config: d.Config.DeepCopy(), }); err != nil { - return result, fmt.Errorf("failed to run step %q: %w", d.Step, err) + return result, fmt.Errorf("failed to run step %q: %w", d.Directive, err) } } } diff --git a/internal/directives/engine_test.go b/internal/directives/engine_test.go index cc8d49d90..3d46ca128 100644 --- a/internal/directives/engine_test.go +++ b/internal/directives/engine_test.go @@ -12,19 +12,19 @@ import ( func TestEngine_Execute(t *testing.T) { tests := []struct { name string - directives []Directive - initRegistry func() StepRegistry + directives []Step + initRegistry func() DirectiveRegistry ctx context.Context assertions func(t *testing.T, result Result, err error) }{ { name: "success: single directive", - directives: []Directive{ - {Step: "mock"}, + directives: []Step{ + {Directive: "mock"}, }, - initRegistry: func() StepRegistry { - registry := make(StepRegistry) - registry.RegisterStep(&mockStep{ + initRegistry: func() DirectiveRegistry { + registry := make(DirectiveRegistry) + registry.RegisterDirective(&mockDirective{ name: "mock", runResult: ResultSuccess, }) @@ -38,17 +38,17 @@ func TestEngine_Execute(t *testing.T) { }, { name: "success: multiple directives", - directives: []Directive{ - {Step: "mock1"}, - {Step: "mock2"}, + directives: []Step{ + {Directive: "mock1"}, + {Directive: "mock2"}, }, - initRegistry: func() StepRegistry { - registry := make(StepRegistry) - registry.RegisterStep(&mockStep{ + initRegistry: func() DirectiveRegistry { + registry := make(DirectiveRegistry) + registry.RegisterDirective(&mockDirective{ name: "mock1", runResult: ResultSuccess, }) - registry.RegisterStep(&mockStep{ + registry.RegisterDirective(&mockDirective{ name: "mock2", runResult: ResultSuccess, }) @@ -61,12 +61,12 @@ func TestEngine_Execute(t *testing.T) { }, }, { - name: "failure: step not found", - directives: []Directive{ - {Step: "unknown"}, + name: "failure: directive not found", + directives: []Step{ + {Directive: "unknown"}, }, - initRegistry: func() StepRegistry { - return make(StepRegistry) + initRegistry: func() DirectiveRegistry { + return make(DirectiveRegistry) }, ctx: context.Background(), assertions: func(t *testing.T, result Result, err error) { @@ -75,13 +75,13 @@ func TestEngine_Execute(t *testing.T) { }, }, { - name: "failure: step returns error", - directives: []Directive{ - {Step: "failing", Alias: "alias1", Config: map[string]any{"key": "value"}}, + name: "failure: directive returns error", + directives: []Step{ + {Directive: "failing", Alias: "alias1", Config: map[string]any{"key": "value"}}, }, - initRegistry: func() StepRegistry { - registry := make(StepRegistry) - registry.RegisterStep(&mockStep{ + initRegistry: func() DirectiveRegistry { + registry := make(DirectiveRegistry) + registry.RegisterDirective(&mockDirective{ name: "failing", runResult: ResultFailure, runErr: errors.New("something went wrong"), @@ -96,13 +96,13 @@ func TestEngine_Execute(t *testing.T) { }, { name: "failure: context canceled", - directives: []Directive{ - {Step: "mock"}, - {Step: "mock"}, // This step should not be executed + directives: []Step{ + {Directive: "mock"}, + {Directive: "mock"}, // This directive should not be executed }, - initRegistry: func() StepRegistry { - registry := make(StepRegistry) - registry.RegisterStep(&mockStep{ + initRegistry: func() DirectiveRegistry { + registry := make(DirectiveRegistry) + registry.RegisterDirective(&mockDirective{ name: "mock", runFunc: func(ctx context.Context, _ *StepContext) (Result, error) { <-ctx.Done() // Wait for context to be canceled diff --git a/internal/directives/mock_directive_test.go b/internal/directives/mock_directive_test.go new file mode 100644 index 000000000..677f41ed7 --- /dev/null +++ b/internal/directives/mock_directive_test.go @@ -0,0 +1,30 @@ +package directives + +import "context" + +// mockDirective is a mock implementation of the Directive interface, which can be +// used for testing. +type mockDirective struct { + // name is the name of the Directive. + name string + // runFunc is the function that the step should call when Run is called. + // If set, this function will be called instead of returning runResult + // and runErr. + runFunc func(context.Context, *StepContext) (Result, error) + // runResult is the result that the Directive should return when Run is + // called. + runResult Result + // runErr is the error that the Directive should return when Run is called. + runErr error +} + +func (d *mockDirective) Name() string { + return d.name +} + +func (d *mockDirective) Run(ctx context.Context, stepCtx *StepContext) (Result, error) { + if d.runFunc != nil { + return d.runFunc(ctx, stepCtx) + } + return d.runResult, d.runErr +} diff --git a/internal/directives/mock_step_test.go b/internal/directives/mock_step_test.go deleted file mode 100644 index 482566e30..000000000 --- a/internal/directives/mock_step_test.go +++ /dev/null @@ -1,29 +0,0 @@ -package directives - -import "context" - -// mockStep is a mock implementation of the Step interface, which can be -// used for testing. -type mockStep struct { - // name is the name of the step. - name string - // runFunc is the function that the step should call when Run is called. - // If set, this function will be called instead of returning runResult - // and runErr. - runFunc func(context.Context, *StepContext) (Result, error) - // runResult is the result that the step should return when Run is called. - runResult Result - // runErr is the error that the step should return when Run is called. - runErr error -} - -func (d *mockStep) Name() string { - return d.name -} - -func (d *mockStep) Run(ctx context.Context, stepCtx *StepContext) (Result, error) { - if d.runFunc != nil { - return d.runFunc(ctx, stepCtx) - } - return d.runResult, d.runErr -} diff --git a/internal/directives/registry.go b/internal/directives/registry.go index 8fb434e6a..3979bd033 100644 --- a/internal/directives/registry.go +++ b/internal/directives/registry.go @@ -5,30 +5,31 @@ import ( "maps" ) -// builtins is the registry of built-in steps. -var builtins = StepRegistry{} +// builtins is the registry of built-in directives. +var builtins = DirectiveRegistry{} -// BuiltinsRegistry returns a registry of built-in steps. -func BuiltinsRegistry() StepRegistry { +// BuiltinsRegistry returns a registry of built-in directives. +func BuiltinsRegistry() DirectiveRegistry { return maps.Clone(builtins) } -// StepRegistry is a map of step names to steps. It is used to register and -// retrieve steps by name. -type StepRegistry map[string]Step +// DirectiveRegistry is a map of directive names to directives. It is used to +// register and retrieve directives by name. +type DirectiveRegistry map[string]Directive -// RegisterStep registers a step with the given name. If a step with the same -// name has already been registered, it will be overwritten. -func (r StepRegistry) RegisterStep(step Step) { - r[step.Name()] = step + +// RegisterDirective registers a Directive with the given name. If a Directive +// with the same name has already been registered, it will be overwritten. +func (r DirectiveRegistry) RegisterDirective(directive Directive) { + r[directive.Name()] = directive } -// GetStep returns the step with the given name, or an error if no such step -// exists. -func (r StepRegistry) GetStep(name string) (Step, error) { +// GetDirective returns the Directive with the given name, or an error if no +// such Directive exists. +func (r DirectiveRegistry) GetDirective(name string) (Directive, error) { step, ok := r[name] if !ok { - return nil, fmt.Errorf("step %q not found", name) + return nil, fmt.Errorf("directive %q not found", name) } return step, nil } diff --git a/internal/directives/registry_test.go b/internal/directives/registry_test.go index ab0234df1..6296517bb 100644 --- a/internal/directives/registry_test.go +++ b/internal/directives/registry_test.go @@ -7,43 +7,43 @@ import ( "github.com/stretchr/testify/assert" ) -func TestStepRegistry_RegisterStep(t *testing.T) { - t.Run("registers step", func(t *testing.T) { - r := make(StepRegistry) - s := &mockStep{} - r.RegisterStep(s) +func TestDirectiveRegistry_RegisterDirective(t *testing.T) { + t.Run("registers directive", func(t *testing.T) { + r := make(DirectiveRegistry) + s := &mockDirective{} + r.RegisterDirective(s) assert.Equal(t, s, r[s.Name()]) }) - t.Run("overwrites step", func(t *testing.T) { - r := make(StepRegistry) - s := &mockStep{} - r.RegisterStep(s) - s2 := &mockStep{ + t.Run("overwrites directive", func(t *testing.T) { + r := make(DirectiveRegistry) + s := &mockDirective{} + r.RegisterDirective(s) + s2 := &mockDirective{ runErr: fmt.Errorf("error"), } - r.RegisterStep(s2) + r.RegisterDirective(s2) assert.NotEqual(t, s, r[s2.Name()]) assert.Equal(t, s2, r[s2.Name()]) }) } -func TestStepRegistry_GetStep(t *testing.T) { - t.Run("step exists", func(t *testing.T) { - r := make(StepRegistry) - s := &mockStep{} - r.RegisterStep(s) +func TestDirectiveRegistry_GetDirective(t *testing.T) { + t.Run("directive exists", func(t *testing.T) { + r := make(DirectiveRegistry) + s := &mockDirective{} + r.RegisterDirective(s) - step, err := r.GetStep(s.Name()) + step, err := r.GetDirective(s.Name()) assert.NoError(t, err) assert.Equal(t, s, step) }) - t.Run("step does not exist", func(t *testing.T) { - r := make(StepRegistry) - _, err := r.GetStep("nonexistent") + t.Run("directive does not exist", func(t *testing.T) { + r := make(DirectiveRegistry) + _, err := r.GetDirective("nonexistent") assert.ErrorContains(t, err, "not found") }) }