@@ -25,8 +25,36 @@ http get with retry:
2525 return nil
2626 },
2727 )
28+ if err != nil {
29+ // handle error
30+ }
31+
32+ fmt.Println(string(body))
2833
29- fmt.Println(body)
34+ http get with retry with data:
35+
36+ url := "http://example.com"
37+
38+ body, err := retry.DoWithData(
39+ func() ([]byte, error) {
40+ resp, err := http.Get(url)
41+ if err != nil {
42+ return nil, err
43+ }
44+ defer resp.Body.Close()
45+ body, err := ioutil.ReadAll(resp.Body)
46+ if err != nil {
47+ return nil, err
48+ }
49+
50+ return body, nil
51+ },
52+ )
53+ if err != nil {
54+ // handle error
55+ }
56+
57+ fmt.Println(string(body))
3058
3159[next examples](https://github.com/avast/retry-go/tree/master/examples)
3260
@@ -72,6 +100,9 @@ import (
72100// Function signature of retryable function
73101type RetryableFunc func () error
74102
103+ // Function signature of retryable function with data
104+ type RetryableFuncWithData [T any ] func () (T , error )
105+
75106// Default timer is a wrapper around time.After
76107type timerImpl struct {}
77108
@@ -80,7 +111,17 @@ func (t *timerImpl) After(d time.Duration) <-chan time.Time {
80111}
81112
82113func Do (retryableFunc RetryableFunc , opts ... Option ) error {
114+ retryableFuncWithData := func () (any , error ) {
115+ return nil , retryableFunc ()
116+ }
117+
118+ _ , err := DoWithData (retryableFuncWithData , opts ... )
119+ return err
120+ }
121+
122+ func DoWithData [T any ](retryableFunc RetryableFuncWithData [T ], opts ... Option ) (T , error ) {
83123 var n uint
124+ var emptyT T
84125
85126 // default
86127 config := newDefaultRetryConfig ()
@@ -91,19 +132,24 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
91132 }
92133
93134 if err := config .context .Err (); err != nil {
94- return err
135+ return emptyT , err
95136 }
96137
97138 // Setting attempts to 0 means we'll retry until we succeed
98139 var lastErr error
99140 if config .attempts == 0 {
100- for err := retryableFunc (); err != nil ; err = retryableFunc () {
141+ for {
142+ t , err := retryableFunc ()
143+ if err == nil {
144+ return t , nil
145+ }
146+
101147 if ! IsRecoverable (err ) {
102- return err
148+ return emptyT , err
103149 }
104150
105151 if ! config .retryIf (err ) {
106- return err
152+ return emptyT , err
107153 }
108154
109155 lastErr = err
@@ -114,81 +160,66 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
114160 case <- config .timer .After (delay (config , n , err )):
115161 case <- config .context .Done ():
116162 if config .wrapContextErrorWithLastError {
117- return Error {config .context .Err (), lastErr }
163+ return emptyT , Error {config .context .Err (), lastErr }
118164 }
119- return config .context .Err ()
165+ return emptyT , config .context .Err ()
120166 }
121167 }
122-
123- return nil
124168 }
125169
126- var errorLog Error
127- if ! config .lastErrorOnly {
128- errorLog = make (Error , config .attempts )
129- } else {
130- errorLog = make (Error , 1 )
131- }
170+ errorLog := Error {}
132171
133172 attemptsForError := make (map [error ]uint , len (config .attemptsForError ))
134173 for err , attempts := range config .attemptsForError {
135174 attemptsForError [err ] = attempts
136175 }
137176
138- lastErrIndex := n
139177 shouldRetry := true
140178 for shouldRetry {
141- err := retryableFunc ()
179+ t , err := retryableFunc ()
180+ if err == nil {
181+ return t , nil
182+ }
142183
143- if err != nil {
144- errorLog [lastErrIndex ] = unpackUnrecoverable (err )
184+ errorLog = append (errorLog , unpackUnrecoverable (err ))
145185
146- if ! config .retryIf (err ) {
147- break
148- }
186+ if ! config .retryIf (err ) {
187+ break
188+ }
149189
150- config .onRetry (n , err )
190+ config .onRetry (n , err )
151191
152- for errToCheck , attempts := range attemptsForError {
153- if errors .Is (err , errToCheck ) {
154- attempts --
155- attemptsForError [errToCheck ] = attempts
156- shouldRetry = shouldRetry && attempts > 0
157- }
192+ for errToCheck , attempts := range attemptsForError {
193+ if errors .Is (err , errToCheck ) {
194+ attempts --
195+ attemptsForError [errToCheck ] = attempts
196+ shouldRetry = shouldRetry && attempts > 0
158197 }
198+ }
159199
160- // if this is last attempt - don't wait
161- if n == config .attempts - 1 {
162- break
163- }
200+ // if this is last attempt - don't wait
201+ if n == config .attempts - 1 {
202+ break
203+ }
164204
165- select {
166- case <- config .timer .After (delay (config , n , err )):
167- case <- config .context .Done ():
168- if config .lastErrorOnly {
169- return config .context .Err ()
170- }
171- n ++
172- errorLog [n ] = config .context .Err ()
173- return errorLog [:lenWithoutNil (errorLog )]
205+ select {
206+ case <- config .timer .After (delay (config , n , err )):
207+ case <- config .context .Done ():
208+ if config .lastErrorOnly {
209+ return emptyT , config .context .Err ()
174210 }
175211
176- } else {
177- return nil
212+ return emptyT , append (errorLog , config .context .Err ())
178213 }
179214
180215 n ++
181216 shouldRetry = shouldRetry && n < config .attempts
182-
183- if ! config .lastErrorOnly {
184- lastErrIndex = n
185- }
186217 }
187218
188219 if config .lastErrorOnly {
189- return errorLog [ lastErrIndex ]
220+ return emptyT , errorLog . Unwrap ()
190221 }
191- return errorLog [: lenWithoutNil ( errorLog )]
222+ return emptyT , errorLog
192223}
193224
194225func newDefaultRetryConfig () * Config {
@@ -212,7 +243,7 @@ type Error []error
212243// Error method return string representation of Error
213244// It is an implementation of error interface
214245func (e Error ) Error () string {
215- logWithNumber := make ([]string , lenWithoutNil (e ))
246+ logWithNumber := make ([]string , len (e ))
216247 for i , l := range e {
217248 if l != nil {
218249 logWithNumber [i ] = fmt .Sprintf ("#%d: %s" , i + 1 , l .Error ())
@@ -256,17 +287,7 @@ When you need to unwrap all errors, you should use `WrappedErrors()` instead.
256287Added in version 4.2.0.
257288*/
258289func (e Error ) Unwrap () error {
259- return e [lenWithoutNil (e )- 1 ]
260- }
261-
262- func lenWithoutNil (e Error ) (count int ) {
263- for _ , v := range e {
264- if v != nil {
265- count ++
266- }
267- }
268-
269- return
290+ return e [len (e )- 1 ]
270291}
271292
272293// WrappedErrors returns the list of errors that this Error is wrapping.
0 commit comments