aboutsummaryrefslogblamecommitdiffstats
path: root/library/go/core/xerrors/sentinel.go
blob: 6651588619d8cf8948cc41d231bae783c7241044 (plain) (tree)




















































































































































                                                                                                    
package xerrors

import (
	"errors"
	"fmt"
	"io"
	"strings"

	"github.com/ydb-platform/ydb/library/go/x/xreflect"
	"github.com/ydb-platform/ydb/library/go/x/xruntime"
)

// NewSentinel acts as New but does not add stack frame
func NewSentinel(text string) *Sentinel {
	return &Sentinel{error: errors.New(text)}
}

// Sentinel error
type Sentinel struct {
	error
}

// WithFrame adds stack frame to sentinel error (DEPRECATED)
func (s *Sentinel) WithFrame() error {
	return &sentinelWithStackTrace{
		err:        s,
		stacktrace: newStackTrace(1, nil),
	}
}

func (s *Sentinel) WithStackTrace() error {
	return &sentinelWithStackTrace{
		err:        s,
		stacktrace: newStackTrace(1, nil),
	}
}

// Wrap error with this sentinel error. Adds stack frame.
func (s *Sentinel) Wrap(err error) error {
	if err == nil {
		panic("tried to wrap a nil error")
	}

	return &sentinelWrapper{
		err:        s,
		wrapped:    err,
		stacktrace: newStackTrace(1, err),
	}
}

type sentinelWithStackTrace struct {
	err        error
	stacktrace *xruntime.StackTrace
}

func (e *sentinelWithStackTrace) Error() string {
	return e.err.Error()
}

func (e *sentinelWithStackTrace) Format(s fmt.State, v rune) {
	switch v {
	case 'v':
		if s.Flag('+') && e.stacktrace != nil {
			msg := e.err.Error()
			_, _ = io.WriteString(s, msg)
			writeMsgAndStackTraceSeparator(s, msg)
			writeStackTrace(s, e.stacktrace)
			return
		}
		fallthrough
	case 's':
		_, _ = io.WriteString(s, e.err.Error())
	case 'q':
		_, _ = fmt.Fprintf(s, "%q", e.err.Error())
	}
}

func writeMsgAndStackTraceSeparator(w io.Writer, msg string) {
	separator := "\n"
	if !strings.HasSuffix(msg, ":") {
		separator = ":\n"
	}

	_, _ = io.WriteString(w, separator)
}

// Is checks if e holds the specified error. Checks only immediate error.
func (e *sentinelWithStackTrace) Is(target error) bool {
	return e.err == target
}

// As checks if ew holds the specified error type. Checks only immediate error.
// It does NOT perform target checks as it relies on errors.As to do it
func (e *sentinelWithStackTrace) As(target interface{}) bool {
	return xreflect.Assign(e.err, target)
}

type sentinelWrapper struct {
	err        error
	wrapped    error
	stacktrace *xruntime.StackTrace
}

func (e *sentinelWrapper) Error() string {
	return fmt.Sprintf("%s", e)
}

func (e *sentinelWrapper) Format(s fmt.State, v rune) {
	switch v {
	case 'v':
		if s.Flag('+') {
			if e.stacktrace != nil {
				msg := e.err.Error()
				_, _ = io.WriteString(s, msg)
				writeMsgAndStackTraceSeparator(s, msg)
				writeStackTrace(s, e.stacktrace)
				_, _ = fmt.Fprintf(s, "%+v", e.wrapped)
			} else {
				_, _ = io.WriteString(s, e.err.Error())
				_, _ = io.WriteString(s, ": ")
				_, _ = fmt.Fprintf(s, "%+v", e.wrapped)
			}

			return
		}
		fallthrough
	case 's':
		_, _ = io.WriteString(s, e.err.Error())
		_, _ = io.WriteString(s, ": ")
		_, _ = io.WriteString(s, e.wrapped.Error())
	case 'q':
		_, _ = fmt.Fprintf(s, "%q", fmt.Sprintf("%s: %s", e.err.Error(), e.wrapped.Error()))
	}
}

// Unwrap implements Wrapper interface
func (e *sentinelWrapper) Unwrap() error {
	return e.wrapped
}

// Is checks if ew holds the specified error. Checks only immediate error.
func (e *sentinelWrapper) Is(target error) bool {
	return e.err == target
}

// As checks if error holds the specified error type. Checks only immediate error.
// It does NOT perform target checks as it relies on errors.As to do it
func (e *sentinelWrapper) As(target interface{}) bool {
	return xreflect.Assign(e.err, target)
}