@@ -15,22 +15,99 @@ import (
1515 "github.com/ARM-software/golang-utils/utils/reflection"
1616)
1717
18- func newFunctionStore [T any ](clearOnExecution , stopOnFirstError bool , executeFunc func (context.Context , T ) error ) * store [T ] {
18+ type StoreOptions struct {
19+ clearOnExecution bool
20+ stopOnFirstError bool
21+ sequential bool
22+ reverse bool
23+ }
24+
25+ type StoreOption func (* StoreOptions ) * StoreOptions
26+
27+ // StopOnFirstError stops store execution on first error.
28+ var StopOnFirstError StoreOption = func (o * StoreOptions ) * StoreOptions {
29+ if o == nil {
30+ return o
31+ }
32+ o .stopOnFirstError = true
33+ return o
34+ }
35+
36+ // ExecuteAll executes all functions in the store even if an error is raised. the first error raised is then returned.
37+ var ExecuteAll StoreOption = func (o * StoreOptions ) * StoreOptions {
38+ if o == nil {
39+ return o
40+ }
41+ o .stopOnFirstError = false
42+ return o
43+ }
44+
45+ // ClearAfterExecution clears the store after execution.
46+ var ClearAfterExecution StoreOption = func (o * StoreOptions ) * StoreOptions {
47+ if o == nil {
48+ return o
49+ }
50+ o .clearOnExecution = true
51+ return o
52+ }
53+
54+ // RetainAfterExecution keep the store intact after execution (no reset).
55+ var RetainAfterExecution StoreOption = func (o * StoreOptions ) * StoreOptions {
56+ if o == nil {
57+ return o
58+ }
59+ o .clearOnExecution = false
60+ return o
61+ }
62+
63+ // Parallel ensures every function registered in the store is executed concurrently in the order they were registered.
64+ var Parallel StoreOption = func (o * StoreOptions ) * StoreOptions {
65+ if o == nil {
66+ return o
67+ }
68+ o .sequential = false
69+ return o
70+ }
71+
72+ // Sequential ensures every function registered in the store is executed sequentially in the order they were registered.
73+ var Sequential StoreOption = func (o * StoreOptions ) * StoreOptions {
74+ if o == nil {
75+ return o
76+ }
77+ o .sequential = true
78+ return o
79+ }
80+
81+ // SequentialInReverse ensures every function registered in the store is executed sequentially but in the reverse order they were registered.
82+ var SequentialInReverse StoreOption = func (o * StoreOptions ) * StoreOptions {
83+ if o == nil {
84+ return o
85+ }
86+ o .sequential = true
87+ o .reverse = true
88+ return o
89+ }
90+
91+ func newFunctionStore [T any ](executeFunc func (context.Context , T ) error , options ... StoreOption ) * store [T ] {
92+
93+ opts := & StoreOptions {}
94+
95+ for i := range options {
96+ opts = options [i ](opts )
97+ }
1998 return & store [T ]{
20- mu : deadlock.RWMutex {},
21- functions : make ([]T , 0 ),
22- executeFunc : executeFunc ,
23- clearOnExecution : clearOnExecution ,
24- stopOnFirstError : stopOnFirstError ,
99+ mu : deadlock.RWMutex {},
100+ functions : make ([]T , 0 ),
101+ executeFunc : executeFunc ,
102+ options : * opts ,
25103 }
26104}
27105
28106type store [T any ] struct {
29- mu deadlock.RWMutex
30- functions []T
31- executeFunc func (ctx context.Context , element T ) error
32- clearOnExecution bool
33- stopOnFirstError bool
107+ mu deadlock.RWMutex
108+ functions []T
109+ executeFunc func (ctx context.Context , element T ) error
110+ options StoreOptions
34111}
35112
36113func (s * store [T ]) RegisterFunction (function ... T ) {
@@ -45,32 +122,87 @@ func (s *store[T]) Len() int {
45122 return len (s .functions )
46123}
47124
48- func (s * store [T ]) Execute (ctx context.Context ) error {
125+ func (s * store [T ]) Execute (ctx context.Context ) ( err error ) {
49126 defer s .mu .Unlock ()
50127 s .mu .Lock ()
51128 if reflection .IsEmpty (s .executeFunc ) {
52- return commonerrors .New (commonerrors .ErrUndefined , "the cancel store was not initialised correctly" )
129+ return commonerrors .New (commonerrors .ErrUndefined , "the store was not initialised correctly" )
130+ }
131+
132+ if s .options .sequential {
133+ err = s .executeSequentially (ctx , s .options .stopOnFirstError , s .options .reverse )
134+ } else {
135+ err = s .executeConcurrently (ctx , s .options .stopOnFirstError )
53136 }
137+
138+ if err == nil && s .options .clearOnExecution {
139+ s .functions = make ([]T , 0 , len (s .functions ))
140+ }
141+ return
142+ }
143+
144+ func (s * store [T ]) executeConcurrently (ctx context.Context , stopOnFirstError bool ) error {
54145 g , gCtx := errgroup .WithContext (ctx )
55- if ! s . stopOnFirstError {
146+ if ! stopOnFirstError {
56147 gCtx = ctx
57148 }
58149 g .SetLimit (len (s .functions ))
59150 for i := range s .functions {
60151 g .Go (func () error {
61- err := DetermineContextError (gCtx )
62- if err != nil {
63- return err
64- }
65- return s .executeFunc (gCtx , s .functions [i ])
152+ _ , subErr := s .executeFunction (gCtx , s .functions [i ])
153+ return subErr
66154 })
67155 }
68156
69- err := g .Wait ()
70- if err == nil && s .clearOnExecution {
71- s .functions = make ([]T , 0 , len (s .functions ))
157+ return g .Wait ()
158+ }
159+
160+ func (s * store [T ]) executeSequentially (ctx context.Context , stopOnFirstError , reverse bool ) (err error ) {
161+ err = DetermineContextError (ctx )
162+ if err != nil {
163+ return
164+ }
165+ if reverse {
166+ for i := len (s .functions ) - 1 ; i >= 0 ; i -- {
167+ shouldBreak , subErr := s .executeFunction (ctx , s .functions [i ])
168+ if shouldBreak {
169+ err = subErr
170+ return
171+ }
172+ if subErr != nil && err == nil {
173+ err = subErr
174+ if stopOnFirstError {
175+ return
176+ }
177+ }
178+ }
179+ } else {
180+ for i := range s .functions {
181+ shouldBreak , subErr := s .executeFunction (ctx , s .functions [i ])
182+ if shouldBreak {
183+ err = subErr
184+ return
185+ }
186+ if subErr != nil && err == nil {
187+ err = subErr
188+ if stopOnFirstError {
189+ return
190+ }
191+ }
192+ }
193+ }
194+
195+ return
196+ }
197+
198+ func (s * store [T ]) executeFunction (ctx context.Context , element T ) (shouldBreak bool , err error ) {
199+ err = DetermineContextError (ctx )
200+ if err != nil {
201+ shouldBreak = true
202+ return
72203 }
73- return err
204+ err = s .executeFunc (ctx , element )
205+ return
74206}
75207
76208type CancelFunctionStore struct {
@@ -90,12 +222,12 @@ func (s *CancelFunctionStore) Len() int {
90222 return s .store .Len ()
91223}
92224
93- // NewCancelFunctionsStore creates a store for cancel functions.
94- func NewCancelFunctionsStore () * CancelFunctionStore {
225+ // NewCancelFunctionsStore creates a store for cancel functions. Whatever the options passed, all cancel functions will be executed.
226+ func NewCancelFunctionsStore (options ... StoreOption ) * CancelFunctionStore {
95227 return & CancelFunctionStore {
96- store : * newFunctionStore [context.CancelFunc ](true , false , func (_ context.Context , cancelFunc context.CancelFunc ) error {
228+ store : * newFunctionStore [context.CancelFunc ](func (_ context.Context , cancelFunc context.CancelFunc ) error {
97229 cancelFunc ()
98230 return nil
99- }),
231+ }, append ( options , ClearAfterExecution , ExecuteAll ) ... ),
100232 }
101233}
0 commit comments