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() }