-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathrepeat.go
165 lines (143 loc) · 4.02 KB
/
repeat.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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package repeat
import (
"context"
)
var (
def = NewRepeater()
)
// Once composes the operations and executes the result once.
//
// It is guaranteed that the first op will be called at least once.
func Once(ops ...Operation) error {
return def.Once(ops...)
}
// Repeat repeat operations until one of them stops the repetition.
//
// It is guaranteed that the first op will be called at least once.
func Repeat(ops ...Operation) error {
return def.Repeat(ops...)
}
// FnRepeat is a Repeat operation.
func FnRepeat(ops ...Operation) Operation {
return def.FnRepeat(ops...)
}
// Compose composes all passed operations into a single one.
func Compose(ops ...Operation) Operation {
return def.Compose(ops...)
}
// WithContext repeat operations until one of them stops the
// repetition or context will be canceled.
//
// It is guaranteed that the first op will be called at least once.
func WithContext(ctx context.Context) Repeater {
return Wrap(WrStopOnContextError(ctx))
}
// Repeater represents general package concept.
type Repeater interface {
Once(...Operation) error
Repeat(...Operation) error
Compose(...Operation) Operation
FnRepeat(...Operation) Operation
}
type stdRepeater struct {
opw OpWrapper
copw OpWrapper
}
// NewRepeater sets up everything to be able to repeat operations.
func NewRepeater() Repeater {
return NewRepeaterExt(Forward, Forward)
}
// Wrap returns object that wraps all repeating ops with passed OpWrapper.
func Wrap(opw OpWrapper) Repeater {
return NewRepeaterExt(opw, Forward)
}
// WrapOnce returns object that wraps all repeating ops combined into a single
// op with passed OpWrapper calling it once.
func WrapOnce(copw OpWrapper) Repeater {
return NewRepeaterExt(Forward, copw)
}
// NewRepeaterExt returns object that wraps all ops with with the given opw
// and wraps composed operation with the given copw.
func NewRepeaterExt(opw, copw OpWrapper) Repeater {
return &stdRepeater{opw, copw}
}
// Cpp returns object that calls C (constructor) at first, then ops,
// then D (destructor). D will be called in any case if C returns nil.
//
// Note! Cpp panics if D returns non nil error. Wrap it using Done if
// you log D's error or handle it somehow else.
//
func Cpp(c, d Operation) Repeater {
return NewRepeaterExt(Forward, WrWith(c, func(e error) error {
_ = FnPanic(d)(e)
return e
}))
}
// With returns object that calls C (constructor) at first, then ops,
// then D (destructor). D will be called in any case if C returns nil.
//
// Note! D is able to hide original error an return nil or return error
// event if the original error is nil.
func With(c, d Operation) Repeater {
return NewRepeaterExt(Forward, WrWith(c, d))
}
// Once composes the operations and executes the result once.
//
// It is guaranteed that the first op will be called at least once.
func (w *stdRepeater) Once(ops ...Operation) error {
return Cause(w.Compose(ops...)(nil))
}
// Repeat repeat operations until one of them stops the repetition.
//
// It is guaranteed that the first op will be called at least once.
func (w *stdRepeater) Repeat(ops ...Operation) error {
return Cause(w.FnRepeat(ops...)(nil))
}
// FnRepeat is a Repeat operation.
func (w *stdRepeater) FnRepeat(ops ...Operation) Operation {
return func(e error) (err error) {
op := w.Compose(ops...)
for {
err = op(e)
switch typedError := err.(type) {
case nil:
e = nil
case *TemporaryError:
e = err
case *StopError:
switch typedError.Cause {
case nil:
return nil
default:
return err
}
default:
return err
}
}
}
}
// Compose wraps ops with wop and composes all passed operations info
// a single one.
func (w *stdRepeater) Compose(ops ...Operation) Operation {
return w.copw(func(e error) (err error) {
for _, op := range ops {
err = w.opw(op)(e)
switch err.(type) {
// Replace last E with nil.
case nil:
e = nil
// Replace last E with new temporary error.
case *TemporaryError:
e = err
// Stop.
case *StopError:
return err
// Stop.
default:
return err
}
}
return e
})
}