package flaky

import (
	
	
)

// PermanentError indicates that a permanent error has been encountered during a
// retry attempt.
type PermanentError struct {
	err    error
	unwrap bool
}

// Internal marks an error as a permanent internal error. Executors unwrap
// internal errors prior to returning them.
func ( error) *PermanentError {
	return &PermanentError{, true}
}

// Permanent marks an error as permanent. That is, returning permanent error
// from retry attempt would stop the retry process.
func ( error) *PermanentError {
	return &PermanentError{, false}
}

// IsPermanentError returns whether err’s error chain contains PermanentError.
func ( error) bool {
	,  := AsPermanentError()
	return 
}

// AsPermanentError attempts to extract PermanentError from err’s error chain.
func ( error) (*PermanentError, bool) {
	var  *PermanentError
	 := errors.As(, &)
	return , 
}

// Error implements the error interface.
func ( *PermanentError) () string {
	return "permanent: " + .err.Error()
}

// Unwrap unwraps the underlying error.
func ( *PermanentError) () error {
	return .err
}

// Internal returns whether the permanent error is internal. It is guaranteed
// that Executor’s Execute method does not return internal errors.
func ( *PermanentError) () bool {
	return .unwrap
}

// unwrapInternal unwraps the underlying error of an internal PermanentError
// from err’s error chain.
func ( error) error {
	,  := AsPermanentError()
	if ! {
		return 
	}
	if !.Internal() {
		return 
	}
	return .Unwrap()
}

type untilPermanentExecutor struct{}

// UntilPermanent returns a new executor that retries operations until either a
// permanent error or context expiration.
func () Executor {
	return (*untilPermanentExecutor)(nil)
}

// Execute implements the Executor interface.
func (*untilPermanentExecutor) ( context.Context,  Op) error {
	var  error
	for {
		 = ()
		if  == nil {
			return nil
		}
		if IsPermanentError() {
			return unwrapInternal()
		}
		if .Err() != nil {
			break
		}
	}
	return 
}