diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..49fabdf --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,24 @@ +# MIT License + +Copyright (c) 2024 Osama Adil + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Package: github.com/phr3nzy/tango +Contact: diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e5e57df --- /dev/null +++ b/Makefile @@ -0,0 +1,67 @@ +# Description: Makefile for the project + +# Variables +BINARY_NAME=tango +VERSION=0.1.0 +BUILD=`date +%FT%T%z` + +# Build for all platforms +build-all: build-linux build-windows build-macos + +# Build the project +build: + @echo "Building the project" + @go build -o "$(BINARY_NAME)" -ldflags "-X main.Version=$(VERSION) -X main.Build=$(BUILD)" ./... + +# Build for Linux optimized +build-linux: + @echo "Building the project for Linux" + @GOOS=linux GOARCH=amd64 go build -o "$(BINARY_NAME)-linux" -ldflags "-X main.Version=$(VERSION) -X main.Build=$(BUILD)" ./... + +# Build for Windows optimized +build-windows: + @echo "Building the project for Windows" + @GOOS=windows GOARCH=amd64 go build -o "$(BINARY_NAME)-windows".exe -ldflags "-X main.Version=$(VERSION) -X main.Build=$(BUILD)" ./... + +# Build for MacOS optimized +build-macos: + @echo "Building the project for MacOS" + @GOOS=darwin GOARCH=amd64 go build -o "$(BINARY_NAME)-macos" -ldflags "-X main.Version=$(VERSION) -X main.Build=$(BUILD)" ./... + +# Run the project +run: + @echo "Running the project" + @go run main.go + +# Clean the project +clean: + @echo "Cleaning the project" + @go clean + @rm -f $(BINARY_NAME) + +# Test the project +test: + @echo "Running tests" + @go test ./... + +# Test the project with coverage +test-coverage: + @echo "Running coverage tests" + @go test -cover ./... + +# Test the project with coverage and generate HTML report +test-html-coverage: + @echo "Running coverage tests" + @go test -coverprofile=coverage.out ./... + @go tool cover -html=coverage.out + @rm coverage.out + +# Benchmark the project +benchmark: + @echo "Running benchmarks" + @go test -bench ./... + +# Benchmark the project with memory profiling +benchmark-mem: + @echo "Running memory benchmarks" + @go test -bench . -benchmem diff --git a/machine.go b/machine.go index d6bb80a..48df866 100644 --- a/machine.go +++ b/machine.go @@ -5,36 +5,40 @@ import ( "sync" ) -type MachineContext[S, T any] struct { - Services S - PreviousResult *StepResponse[S, T] - State T - Machine *Machine[S, T] +// ResponseStatus is a type that represents the status of a response. +type MachineContext[Services, State any] struct { + Services Services + PreviousResult *Response[Services, State] + State State + Machine *Machine[Services, State] } -type MachineConfig[S, T any] struct { +// Plugin is an interface that represents a machine plugin. +type MachineConfig[Services, State any] struct { Log bool LogLevel string - Plugins []Plugin[S, T] + Plugins []Plugin[Services, State] } -type Machine[S, T any] struct { +// Machine is a struct that represents a machine. +type Machine[Services, State any] struct { Name string - Context *MachineContext[S, T] - Steps []Step[S, T] - ExecutedSteps []Step[S, T] - InitialContext *MachineContext[S, T] - Config *MachineConfig[S, T] + Context *MachineContext[Services, State] + Steps []Step[Services, State] + ExecutedSteps []Step[Services, State] + InitialContext *MachineContext[Services, State] + Config *MachineConfig[Services, State] mu sync.Mutex } -func NewMachine[S, T any]( +// NewMachine creates a new machine. +func NewMachine[Services, State any]( name string, - steps []Step[S, T], - initialContext *MachineContext[S, T], - config *MachineConfig[S, T], -) *Machine[S, T] { - m := &Machine[S, T]{ + steps []Step[Services, State], + initialContext *MachineContext[Services, State], + config *MachineConfig[Services, State], +) *Machine[Services, State] { + m := &Machine[Services, State]{ Name: name, Steps: steps, InitialContext: initialContext, @@ -45,17 +49,20 @@ func NewMachine[S, T any]( return m } -func (m *Machine[S, T]) AddStep(step Step[S, T]) { +// AddStep adds a step to the machine. +func (m *Machine[Services, State]) AddStep(step Step[Services, State]) { m.Steps = append(m.Steps, step) } -func (m *Machine[S, T]) Reset() { +// Reset resets the machine to its initial state. It clears the context and executed steps. +func (m *Machine[Services, State]) Reset() { m.Steps = nil m.Context = m.InitialContext m.ExecutedSteps = nil } -func (m *Machine[S, T]) Run() (*StepResponse[S, T], error) { +// Run executes the machine steps. +func (m *Machine[Services, State]) Run() (*Response[Services, State], error) { if len(m.Steps) == 0 { return nil, fmt.Errorf("no steps to execute") } @@ -79,7 +86,11 @@ func (m *Machine[S, T]) Run() (*StepResponse[S, T], error) { case DONE: return response, nil case ERROR: - return nil, fmt.Errorf("execution error at %s", step.Name) + cResponse, err := m.Compensate() + if err != nil { + return nil, fmt.Errorf("compensate error: %v", err) + } + return cResponse, fmt.Errorf("step %s failed: %v", step.Name, response.Result) case SKIP: i += response.SkipCount case JUMP: @@ -107,12 +118,18 @@ func (m *Machine[S, T]) Run() (*StepResponse[S, T], error) { return nil, nil } -func (m *Machine[S, T]) executeStep(step Step[S, T]) (*StepResponse[S, T], error) { - +// executeStep runs the step and its before and after functions. +func (m *Machine[Services, State]) executeStep(step Step[Services, State]) (*Response[Services, State], error) { if m.Config.Log { fmt.Printf("Executing step: %s\n", step.Name) } + for _, plugin := range m.Config.Plugins { + if err := plugin.Execute(m.Context); err != nil { + return nil, fmt.Errorf("plugin before step error: %v", err) + } + } + if step.BeforeExecute != nil { if err := step.BeforeExecute(m.Context); err != nil { return nil, err @@ -137,7 +154,8 @@ func (m *Machine[S, T]) executeStep(step Step[S, T]) (*StepResponse[S, T], error return response, nil } -func (m *Machine[S, T]) Compensate() (*StepResponse[S, T], error) { +// Compensate runs the compensate functions of the executed steps. +func (m *Machine[Services, State]) Compensate() (*Response[Services, State], error) { m.Context = m.InitialContext for i := len(m.ExecutedSteps) - 1; i >= 0; i-- { step := m.ExecutedSteps[i] @@ -161,28 +179,35 @@ func (m *Machine[S, T]) Compensate() (*StepResponse[S, T], error) { return nil, nil } +// Result is an alias for any. type Result interface{} -func (m *Machine[S, T]) NewStep(step *Step[S, T]) *Step[S, T] { - return NewStep(step) +// NewStep creates a new step. +func (m *Machine[Services, State]) NewStep(step *Step[Services, State]) { + m.AddStep(*NewStep(step)) } -func (m *Machine[S, T]) Next(result Result) *StepResponse[S, T] { - return Next[Result, S, T](result) +// Next creates a response with status NEXT. +func (m *Machine[Services, State]) Next(result Result) *Response[Services, State] { + return Next[Result, Services, State](result) } -func (m *Machine[S, T]) Done(result Result) *StepResponse[S, T] { - return Done[Result, S, T](result) +// Done creates a response with status DONE. +func (m *Machine[Services, State]) Done(result Result) *Response[Services, State] { + return Done[Result, Services, State](result) } -func (m *Machine[S, T]) Error(result Result) *StepResponse[S, T] { - return Error[Result, S, T](result) +// Error creates a response with status ERROR. +func (m *Machine[Services, State]) Error(result Result) *Response[Services, State] { + return Error[Result, Services, State](result) } -func (m *Machine[S, T]) Skip(result Result, count int) *StepResponse[S, T] { - return Skip[Result, S, T](result, count) +// Skip creates a response with status SKIP. +func (m *Machine[Services, State]) Skip(result Result, count int) *Response[Services, State] { + return Skip[Result, Services, State](result, count) } -func (m *Machine[S, T]) Jump(result any, target string) *StepResponse[S, T] { - return Jump[Result, S, T](result, target) +// Jump creates a response with status JUMP. +func (m *Machine[Services, State]) Jump(result any, target string) *Response[Services, State] { + return Jump[Result, Services, State](result, target) } diff --git a/machine_test.go b/machine_test.go index ac7553c..ae2b0ca 100644 --- a/machine_test.go +++ b/machine_test.go @@ -30,13 +30,13 @@ func TestMachine_Run(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Next("Next"), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Done("Done"), nil }, }, @@ -90,7 +90,7 @@ type compensateTestCase struct { name string steps []tango.Step[Services, State] expectedError string - expectedResult *tango.StepResponse[Services, State] + expectedResult *tango.Response[Services, State] expectedStepNames []string } @@ -101,24 +101,24 @@ func TestMachine_Compensate(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Next("Next"), nil }, - Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Done("Compensated"), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Error("I will be compensated"), nil }, - Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Done("Done"), nil }, }, }, - expectedError: "execution error at Step2", + expectedError: "step Step2 failed: I will be compensated", expectedResult: nil, expectedStepNames: []string{"Step1", "Step2"}, }, @@ -158,6 +158,87 @@ func TestMachine_Compensate(t *testing.T) { } } +type compensateStateTestCase struct { + name string + steps []tango.Step[Services, State] + expectedError string + expectedResult *tango.Response[Services, State] + expectedStepNames []string +} + +func TestMachine_Compensate_State(t *testing.T) { + tests := []compensateStateTestCase{ + { + name: "CompensateStateOnError", + steps: []tango.Step[Services, State]{ + { + Name: "Step1", + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { + ctx.State.Counter++ + return ctx.Machine.Next("Next"), nil + }, + Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { + ctx.State.Counter-- + return ctx.Machine.Done("Compensated"), nil + }, + }, + { + Name: "Step2", + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { + ctx.State.Counter++ + return ctx.Machine.Error("I will be compensated"), nil + }, + Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { + ctx.State.Counter-- + return ctx.Machine.Done("Done"), nil + }, + }, + }, + expectedError: "step Step2 failed: I will be compensated", + expectedResult: nil, + expectedStepNames: []string{"Step1", "Step2"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + context := &tango.MachineContext[Services, State]{State: State{Counter: 0}} + m := tango.NewMachine("TestMachine", []tango.Step[Services, State]{}, context, &tango.MachineConfig[Services, State]{ + Log: false, + }) + context.Machine = m + + for _, step := range tt.steps { + m.AddStep(step) + } + + result, err := m.Run() + + if err == nil || err.Error() != tt.expectedError { + t.Errorf("expected error %v, got %v", tt.expectedError, err) + } + if result != tt.expectedResult { + t.Errorf("expected result %v, got %v", tt.expectedResult, result) + } + + if len(m.ExecutedSteps) != len(tt.expectedStepNames) { + t.Errorf("expected %v executed steps, got %v", len(tt.expectedStepNames), len(m.ExecutedSteps)) + } + + for i, step := range m.ExecutedSteps { + if step.Name != tt.expectedStepNames[i] { + t.Errorf("expected step %v, got %v", tt.expectedStepNames[i], step.Name) + } + } + + if m.Context.State.Counter != 0 { + t.Errorf("expected state counter to be 0, got %v", m.Context.State.Counter) + } + + }) + } +} + type resetTestCase struct { name string steps []tango.Step[Services, State] @@ -172,13 +253,13 @@ func TestMachine_Reset(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Next("Next"), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Done("Done"), nil }, }, @@ -229,14 +310,14 @@ func TestMachine_Context_State(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { ctx.State.Counter++ return ctx.Machine.Next("Next"), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { ctx.State.Counter++ return ctx.Machine.Done("Done"), nil }, @@ -282,14 +363,14 @@ func TestMachine_Context_Services(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { ctx.Services.Database = "PostgreSQL" return ctx.Machine.Next("Next"), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { ctx.Services.Database = "SQLite" return ctx.Machine.Done("Done"), nil }, @@ -335,19 +416,19 @@ func TestMachine_Step_Jump(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Jump("Jump", "Step3"), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Error("I got skipped"), nil }, }, { Name: "Step3", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Done("Done"), nil }, }, @@ -415,19 +496,19 @@ func TestMachine_Step_Skip(t *testing.T) { steps: []tango.Step[Services, State]{ { Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Skip("Skip", 1), nil }, }, { Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Error("I will be skipped"), nil }, }, { Name: "Step3", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return ctx.Machine.Done("Done"), nil }, }, @@ -480,20 +561,18 @@ func BenchmarkMachine_Run(b *testing.B) { }) // Add some steps to the machine - step1 := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Next("Next"), nil }, }) - step2 := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Done("Done"), nil }, }) - m.AddStep(*step1) - m.AddStep(*step2) // Run the machine for i := 0; i < b.N; i++ { @@ -508,26 +587,24 @@ func BenchmarkMachine_Compensate(b *testing.B) { }) // Add some steps to the machine - step1 := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Next("Next"), nil }, - Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Done("Compensated"), nil }, }) - step2 := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Error("I will be compensated"), nil }, - Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Compensate: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Done("Done"), nil }, }) - m.AddStep(*step1) - m.AddStep(*step2) // Run the machine for i := 0; i < b.N; i++ { @@ -542,20 +619,18 @@ func BenchmarkMachine_Reset(b *testing.B) { }) // Add some steps to the machine - step1 := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "Step1", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Next("Next"), nil }, }) - step2 := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "Step2", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Done("Done"), nil }, }) - m.AddStep(*step1) - m.AddStep(*step2) // Run the machine _, _ = m.Run() @@ -578,21 +653,20 @@ func BenchmarkMachine_100Steps_Run(b *testing.B) { // Add 100 steps to the machine for i := 0; i < 100; i++ { - step := m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: fmt.Sprintf("Step%d", i), - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Next("Next"), nil }, }) - m.AddStep(*step) } - m.AddStep(*m.NewStep(&tango.Step[Services, State]{ + m.NewStep(&tango.Step[Services, State]{ Name: "LastStep", - Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.StepResponse[Services, State], error) { + Execute: func(ctx *tango.MachineContext[Services, State]) (*tango.Response[Services, State], error) { return m.Done("Done"), nil }, - })) + }) // Run the machine for i := 0; i < b.N; i++ { diff --git a/steps.go b/steps.go index 7e3587d..263da60 100644 --- a/steps.go +++ b/steps.go @@ -1,63 +1,75 @@ package tango -type StepStatus string +// ResponseStatus is a type that represents the status of a response. +type ResponseStatus string +// ResponseStatus is a type that represents the status of a response. const ( - NEXT StepStatus = "NEXT" - DONE StepStatus = "DONE" - ERROR StepStatus = "ERROR" - SKIP StepStatus = "SKIP" - JUMP StepStatus = "JUMP" + NEXT ResponseStatus = "NEXT" + DONE ResponseStatus = "DONE" + ERROR ResponseStatus = "ERROR" + SKIP ResponseStatus = "SKIP" + JUMP ResponseStatus = "JUMP" ) -type StepResponse[S, T any] struct { +// Response is a struct that represents the response of a step execution. +type Response[State, Services any] struct { Result interface{} - Status StepStatus + Status ResponseStatus SkipCount int JumpTarget string - NewMachine *Machine[S, T] // New field to allow nested machine execution + NewMachine *Machine[State, Services] // New field to allow nested machine execution } -func NewStepResponse[Result, S, T any](result Result, status StepStatus, skipCount int, jumpTarget string, newMachine *Machine[S, T]) *StepResponse[S, T] { - return &StepResponse[S, T]{Result: result, Status: status, SkipCount: skipCount, JumpTarget: jumpTarget, NewMachine: newMachine} +// NewResponse creates a new response. +func NewResponse[Result, State, Services any](result Result, status ResponseStatus, skipCount int, jumpTarget string, newMachine *Machine[State, Services]) *Response[State, Services] { + return &Response[State, Services]{Result: result, Status: status, SkipCount: skipCount, JumpTarget: jumpTarget, NewMachine: newMachine} } -func Next[Result, S, T any](result Result) *StepResponse[S, T] { - return NewStepResponse[Result, S, T](result, NEXT, 0, "", nil) +// Next creates a response with status NEXT. +func Next[Result, State, Services any](result Result) *Response[State, Services] { + return NewResponse[Result, State, Services](result, NEXT, 0, "", nil) } -func Done[Result, S, T any](result Result) *StepResponse[S, T] { - return NewStepResponse[Result, S, T](result, DONE, 0, "", nil) +// Done creates a response with status DONE. +func Done[Result, State, Services any](result Result) *Response[State, Services] { + return NewResponse[Result, State, Services](result, DONE, 0, "", nil) } -func Error[Result, S, T any](result Result) *StepResponse[S, T] { - return NewStepResponse[Result, S, T](result, ERROR, 0, "", nil) +// Error creates a response with status ERROR. +func Error[Result, State, Services any](result Result) *Response[State, Services] { + return NewResponse[Result, State, Services](result, ERROR, 0, "", nil) } -func Skip[Result, S, T any](result Result, count int) *StepResponse[S, T] { - return NewStepResponse[Result, S, T](result, SKIP, count, "", nil) +// Skip creates a response with status SKIP. +func Skip[Result, State, Services any](result Result, count int) *Response[State, Services] { + return NewResponse[Result, State, Services](result, SKIP, count, "", nil) } -func Jump[Result, S, T any](result Result, target string) *StepResponse[S, T] { - return NewStepResponse[Result, S, T](result, JUMP, 0, target, nil) +// Jump creates a response with status JUMP. +func Jump[Result, State, Services any](result Result, target string) *Response[State, Services] { + return NewResponse[Result, State, Services](result, JUMP, 0, target, nil) } -func RunNewMachine[Result, S, T any](result Result, newMachine *Machine[S, T]) *StepResponse[S, T] { - return NewStepResponse(result, NEXT, 0, "", newMachine) +// RunNewMachine creates a response with status NEXT and a new machine. +func RunNewMachine[Result, State, Services any](result Result, newMachine *Machine[State, Services]) *Response[State, Services] { + return NewResponse(result, NEXT, 0, "", newMachine) } -type Step[S, T any] struct { +// Step is a struct that represents a step in a machine. +type Step[State, Services any] struct { Name string - Execute func(ctx *MachineContext[S, T]) (*StepResponse[S, T], error) - BeforeExecute func(ctx *MachineContext[S, T]) error - AfterExecute func(ctx *MachineContext[S, T]) error - Compensate func(ctx *MachineContext[S, T]) (*StepResponse[S, T], error) - BeforeCompensate func(ctx *MachineContext[S, T]) error - AfterCompensate func(ctx *MachineContext[S, T]) error + Execute func(ctx *MachineContext[State, Services]) (*Response[State, Services], error) + BeforeExecute func(ctx *MachineContext[State, Services]) error + AfterExecute func(ctx *MachineContext[State, Services]) error + Compensate func(ctx *MachineContext[State, Services]) (*Response[State, Services], error) + BeforeCompensate func(ctx *MachineContext[State, Services]) error + AfterCompensate func(ctx *MachineContext[State, Services]) error } -func NewStep[S, T any](step *Step[S, T]) *Step[S, T] { - return &Step[S, T]{ +// NewStep creates a new step. +func NewStep[State, Services any](step *Step[State, Services]) *Step[State, Services] { + return &Step[State, Services]{ Name: step.Name, Execute: step.Execute, BeforeExecute: step.BeforeExecute,