package flaky

import (
	
	

	
)

// RetryExecutor is an executor that retries executing an operation using the
// provided backoff. It allows executing operations that may succeed after a few
// attempts.
type RetryExecutor struct {
	clock   *clock.Clock
	backoff BackoffProvider
}

// Retry returns a new executor that uses the backoff provider to retry
// operation execution.
//
// It attempts to execute f until a backoff function returns false, an attempt
// returns permanent error or a context expires. On failure it returns the last
// error encountered.
//
// It requests a new backoff function from provider for each Execute invocation.
//
func ( BackoffProvider) *RetryExecutor {
	return &RetryExecutor{
		clock:   clock.System(),
		backoff: ,
	}
}

// WithClock returns a copy of the executor that uses the given clock.
func ( *RetryExecutor) ( *clock.Clock) *RetryExecutor {
	if  == nil {
		 = clock.System()
	}
	return &RetryExecutor{
		clock:   ,
		backoff: .backoff,
	}
}

// Execute implements the Executor interface.
func ( *RetryExecutor) ( context.Context,  Op) error {
	 := .backoff.Backoff()

	var  error
	var  clock.Timer
	for  := uint(0);  < math.MaxUint; ++ {
		 = ()
		if  == nil {
			return nil
		}
		if IsPermanentError() {
			return unwrapInternal()
		}

		if .Err() != nil {
			break
		}

		,  := ()
		if ! {
			break
		}

		if !withinDeadline(, ) {
			break
		}

		if  == nil {
			 = .clock.Timer()
			defer .Stop()
		} else {
			.Reset()
		}
		select {
		case <-.Done():
			return 
		case <-.C():
			continue
		}
	}
	return 
}