package retry import ( "context" "reflect" "time" "github.com/cenkalti/backoff/v4" ) type config struct { maxInterval time.Duration watches []watch backoff.BackOff } // watch is a helper struct to watch multiple channels type watch struct { name string ch reflect.Value fn func(context.Context) error this bool } // Option configures the retry handler type Option func(*config) // WithWatch adds a watch to the retry handler // that will be triggered when a value is received on the channel // and the function will be called, also within a retry handler func WithWatch[T any](name string, ch <-chan T, fn func(context.Context) error) Option { return func(cfg *config) { cfg.watches = append(cfg.watches, watch{name: name, ch: reflect.ValueOf(ch), fn: fn, this: false}) } } // WithMaxInterval sets the upper bound for the retry handler func WithMaxInterval(d time.Duration) Option { return func(cfg *config) { cfg.maxInterval = d } } func newConfig(opts ...Option) ([]watch, backoff.BackOff) { cfg := new(config) for _, opt := range []Option{ WithMaxInterval(time.Minute * 5), } { opt(cfg) } for _, opt := range opts { opt(cfg) } for i, w := range cfg.watches { cfg.watches[i].fn = withRetry(cfg, w) } bo := backoff.NewExponentialBackOff() bo.MaxInterval = cfg.maxInterval bo.MaxElapsedTime = 0 bo.Multiplier = 2 return cfg.watches, bo } func withRetry(cfg *config, w watch) func(context.Context) error { if w.fn == nil { return func(_ context.Context) error { return nil } } return func(ctx context.Context) error { return Retry(ctx, w.name, w.fn, WithMaxInterval(cfg.maxInterval)) } }