-
Notifications
You must be signed in to change notification settings - Fork 3
/
effe.go
150 lines (139 loc) · 5.41 KB
/
effe.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Package effe contains directives for Effe code generation.
// For an overview of working with Effe, see the user guide at
// https://github.com/GettEngineering/effe/blob/master/docs/docs/GettingStarted.md
//
// The directives in this package are used as input to the Effe code generation
// tool. The entry point of Effe's analysis are injector functions: function
// templates denoted by only containing a call to BuildFlow. The arguments to BuildFlow
// describes a set of steps and the Effe code generation tool builds
// calls of steps according to strategy.
package effe
// Based type for declaring steps
type StepFunc interface{}
// Type for declaring business logic for specific case
type CaseKey interface{}
func panicDSLMethodNotFound() {
panic("implementation not generated, run effe before")
}
// Build is placed in the body of an inhector function template to declare the steps.
func BuildFlow(funcs ...StepFunc) interface{} { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// A Step declares a function which will be executed in this place.
// The function should have the following format:
//
// Format:
//
// func step1(dep1 FirstDependency, dep2 SecondDependency) func(req *httpRequest) error {
// return func(req *httpRequest) error {
// return dep1.Send(dep2, req)
// }
// }
//
// Function arguments should be dependencies for your step. It is necessary
// to separate dependencies between steps. You don't need to create big service objects.
// Effe code generation tool calculates all dependencies for your flow and Effe generates
// a service object with dependecies automatically.
// Function return value should be the function which executes here.
// Also, you can call another business flow here. It helps to split and reuse existing business logic.
//
// Examples:
//
// func BuildMyFirstBusinessFlow(){
// effe.BuldFlow(
// effe.Step(step1),
// )
// }
//
// func BuildMySecondBusinessFlow(){
// effe.BuildFlow(
// effe.Step(BuildMyFirstBusinessFlow),
// )
// }
func Step(fn interface{}) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// A Failure works the same way as Step, but with one exception:
// a function executes only if one of steps returns an error.
func Failure(fn interface{}) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// Wrap helps to declare a block of steps which is executed in the following sequence.
// If we declare a function with Before directive, then it will be executed first.
// After that, all functions declared with Step directive will be executed.
// Lastly a function declared with Success directive will be executed.
// Also, we can declare an error handler here with Failure directive.
//
// Examples:
//
// func BuildMyBusinessFlow() {
// effe.BuildFlow(
// effe.Step(step1),
// effe.Step(step2),
// effe.Wrap(effe.Before(lock), effe.Success(unlock), effe.Failure(catchErrAndUnlock),
// effe.Step(step3),
// effe.Step(step4),
// ),
// )
// }
func Wrap(beforeFunc StepFunc, afterFunc StepFunc, steps ...StepFunc) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// This directive helps to declare a function which executes after
// other steps in a directive Wrap and if not one step returned an error.
// This directive can be used only in Wrap.
func Success(fn interface{}) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// This directive helps to declare a function which executes before
// other steps in a directive Wrap.
// This directive can be used only in Wrap.
func Before(fn interface{}) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// Decision directive helps to organize branching of our business logic.
// First argument can be a type or a field from the type.
// Golang doesn't provide an opportunity to pass types to function argument.
// For that you need to create an empty object with your type.
// If you want branching your logic by field from type you can declare it.
// For that you need to create an empty object with your type and get field from it.
// Other arguments declare handlers for every case. Every case can be declared with Case directive.
// Failure directive works here too. With Failure you can declare an error handler for case.
//
// Examples:
//
// func BuildMyBusinessFlow(){
// effe.BuildFlow(
// effe.Step(tryLock),
// effe.Decision(new(Lock),
// effe.Case(true, effe.Step(step1)),
// effe.Case(false, effe.Step(step2)),
// ),
// )
// }
//
// func BuildMyBusinessFlow2(){
// effe.BuildFlow(
// effe.Step(tryLock),
// effe.Decision(Locker{}.Lock,
// effe.Case(true, effe.Step(step1)),
// effe.Case(false, effe.Step(step2)),
// ),
// )
// }
func Decision(tag interface{}, cases ...StepFunc) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}
// This directive can be used only in Decision.
// Case declares the steps which will execute.
func Case(key CaseKey, funcs ...StepFunc) StepFunc { //nolint:unparam
panicDSLMethodNotFound()
return nil
}