Skip to content

Commit 50abccf

Browse files
author
Craig Pastro
committed
Add DoWithData function
1 parent 0407c19 commit 50abccf

File tree

3 files changed

+61
-24
lines changed

3 files changed

+61
-24
lines changed

go.mod

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
module github.com/avast/retry-go/v4
22

3-
go 1.13
3+
go 1.18
44

55
require github.com/stretchr/testify v1.8.1
6+
7+
require (
8+
github.com/davecgh/go-spew v1.1.1 // indirect
9+
github.com/pmezard/go-difflib v1.0.0 // indirect
10+
gopkg.in/yaml.v3 v3.0.1 // indirect
11+
)

retry.go

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,27 @@ slightly inspired by [Try::Tiny::Retry](https://metacpan.org/pod/Try::Tiny::Retr
88
http get with retry:
99
1010
url := "http://example.com"
11-
var body []byte
1211
13-
err := retry.Do(
14-
func() error {
12+
body, err := retry.DoWithData(
13+
func() ([]byte, error) {
1514
resp, err := http.Get(url)
1615
if err != nil {
17-
return err
16+
return nil, err
1817
}
1918
defer resp.Body.Close()
20-
body, err = ioutil.ReadAll(resp.Body)
19+
body, err := ioutil.ReadAll(resp.Body)
2120
if err != nil {
22-
return err
21+
return nil, err
2322
}
2423
25-
return nil
24+
return body, nil
2625
},
2726
)
27+
if err != nil {
28+
// handle error
29+
}
2830
29-
fmt.Println(body)
31+
fmt.Println(string(body))
3032
3133
[next examples](https://github.com/avast/retry-go/tree/master/examples)
3234
@@ -72,6 +74,8 @@ import (
7274
// Function signature of retryable function
7375
type RetryableFunc func() error
7476

77+
type RetryableFuncWithData[T any] func() (T, error)
78+
7579
// Default timer is a wrapper around time.After
7680
type timerImpl struct{}
7781

@@ -80,7 +84,18 @@ func (t *timerImpl) After(d time.Duration) <-chan time.Time {
8084
}
8185

8286
func Do(retryableFunc RetryableFunc, opts ...Option) error {
87+
retryableFuncWithData := func() (any, error) {
88+
err := retryableFunc()
89+
return nil, err
90+
}
91+
92+
_, err := DoWithData(retryableFuncWithData, opts...)
93+
return err
94+
}
95+
96+
func DoWithData[T any](retryableFunc RetryableFuncWithData[T], opts ...Option) (T, error) {
8397
var n uint
98+
var emptyT T
8499

85100
// default
86101
config := newDefaultRetryConfig()
@@ -91,27 +106,30 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
91106
}
92107

93108
if err := config.context.Err(); err != nil {
94-
return err
109+
return emptyT, err
95110
}
96111

97112
// Setting attempts to 0 means we'll retry until we succeed
98113
if config.attempts == 0 {
99-
for err := retryableFunc(); err != nil; err = retryableFunc() {
114+
for {
115+
t, err := retryableFunc()
116+
if err == nil {
117+
return t, nil
118+
}
119+
100120
n++
101121

102122
if !IsRecoverable(err) {
103-
return err
123+
return emptyT, err
104124
}
105125

106126
config.onRetry(n, err)
107127
select {
108128
case <-config.timer.After(delay(config, n, err)):
109129
case <-config.context.Done():
110-
return config.context.Err()
130+
return emptyT, config.context.Err()
111131
}
112132
}
113-
114-
return nil
115133
}
116134

117135
var errorLog Error
@@ -129,7 +147,7 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
129147
lastErrIndex := n
130148
shouldRetry := true
131149
for shouldRetry {
132-
err := retryableFunc()
150+
t, err := retryableFunc()
133151

134152
if err != nil {
135153
errorLog[lastErrIndex] = unpackUnrecoverable(err)
@@ -157,15 +175,15 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
157175
case <-config.timer.After(delay(config, n, err)):
158176
case <-config.context.Done():
159177
if config.lastErrorOnly {
160-
return config.context.Err()
178+
return emptyT, config.context.Err()
161179
}
162180
n++
163181
errorLog[n] = config.context.Err()
164-
return errorLog[:lenWithoutNil(errorLog)]
182+
return emptyT, errorLog[:lenWithoutNil(errorLog)]
165183
}
166184

167185
} else {
168-
return nil
186+
return t, nil
169187
}
170188

171189
n++
@@ -177,9 +195,9 @@ func Do(retryableFunc RetryableFunc, opts ...Option) error {
177195
}
178196

179197
if config.lastErrorOnly {
180-
return errorLog[lastErrIndex]
198+
return emptyT, errorLog[lastErrIndex]
181199
}
182-
return errorLog[:lenWithoutNil(errorLog)]
200+
return emptyT, errorLog[:lenWithoutNil(errorLog)]
183201
}
184202

185203
func newDefaultRetryConfig() *Config {

retry_test.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,15 @@ import (
1111
"github.com/stretchr/testify/assert"
1212
)
1313

14-
func TestDoAllFailed(t *testing.T) {
14+
func TestDoWithDataAllFailed(t *testing.T) {
1515
var retrySum uint
16-
err := Do(
17-
func() error { return errors.New("test") },
16+
v, err := DoWithData(
17+
func() (int, error) { return 7, errors.New("test") },
1818
OnRetry(func(n uint, err error) { retrySum += n }),
1919
Delay(time.Nanosecond),
2020
)
2121
assert.Error(t, err)
22+
assert.Equal(t, 0, v)
2223

2324
expectedErrorFormat := `All attempts fail:
2425
#1: test
@@ -44,7 +45,19 @@ func TestDoFirstOk(t *testing.T) {
4445
)
4546
assert.NoError(t, err)
4647
assert.Equal(t, uint(0), retrySum, "no retry")
48+
}
4749

50+
func TestDoWithDataFirstOk(t *testing.T) {
51+
returnVal := 1
52+
53+
var retrySum uint
54+
val, err := DoWithData(
55+
func() (int, error) { return returnVal, nil },
56+
OnRetry(func(n uint, err error) { retrySum += n }),
57+
)
58+
assert.NoError(t, err)
59+
assert.Equal(t, returnVal, val)
60+
assert.Equal(t, uint(0), retrySum, "no retry")
4861
}
4962

5063
func TestRetryIf(t *testing.T) {

0 commit comments

Comments
 (0)