37 lines
1.1 KiB
Go
37 lines
1.1 KiB
Go
|
|
package usecase
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"errors"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// OperationScope owns an optional deadline and its invocation-specific cause.
|
||
|
|
type OperationScope struct {
|
||
|
|
Context context.Context
|
||
|
|
cancel context.CancelFunc
|
||
|
|
cause error
|
||
|
|
}
|
||
|
|
|
||
|
|
// NewOperationScope creates a child only when the selected duration is positive.
|
||
|
|
func NewOperationScope(parent context.Context, duration time.Duration) *OperationScope {
|
||
|
|
scope := &OperationScope{Context: parent, cancel: func() {}}
|
||
|
|
if duration > 0 {
|
||
|
|
scope.cause = errors.New("operation deadline")
|
||
|
|
scope.Context, scope.cancel = context.WithTimeoutCause(parent, duration, scope.cause)
|
||
|
|
}
|
||
|
|
return scope
|
||
|
|
}
|
||
|
|
|
||
|
|
// Finalize preserves the failure and marks it only when this scope expired.
|
||
|
|
func (s *OperationScope) Finalize(err error) error {
|
||
|
|
if err != nil && s.cause != nil && s.Context.Err() == context.DeadlineExceeded &&
|
||
|
|
errors.Is(err, s.Context.Err()) && context.Cause(s.Context) == s.cause {
|
||
|
|
return &OperationError{Category: ErrOperationTimeout, Cause: err}
|
||
|
|
}
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Close releases the timer and associated child context.
|
||
|
|
func (s *OperationScope) Close() { s.cancel() }
|