// Copyright (c) The EfficientGo Authors.
// Licensed under the Apache License 2.0.

// Initially copied from Thanos and contributed by https://github.com/bisakhmondal.
//
// Copyright (c) The Thanos Authors.
// Licensed under the Apache License 2.0.

package errors

import (
	
	
	
)

// base is the fundamental struct that implements the error interface and the acts as the backbone of this errors package.
type base struct {
	// info contains the error message passed through calls like errors.Wrap, errors.New.
	info string
	// stacktrace stores information about the program counters - i.e. where this error was generated.
	stack stacktrace
	// err is the actual error which is being wrapped with a stacktrace and message information.
	err error
}

// Error implements the error interface.
func ( *base) () string {
	if .err != nil {
		return fmt.Sprintf("%s: %s", .info, .err.Error())
	}
	return .info
}

// Unwrap implements the error Unwrap interface.
func ( *base) () error {
	return .err
}

// Format implements the fmt.Formatter interface to support the formatting of an error chain with "%+v" verb.
// Whenever error is printed with %+v format verb, stacktrace info gets dumped to the output.
func ( *base) ( fmt.State,  rune) {
	if  == 'v' && .Flag('+') {
		.Write([]byte(formatErrorChain()))
		return
	}

	.Write([]byte(.Error()))
}

// New returns a new error with a stacktrace of recent call frames. Each call to New
// returns a distinct error value even if the text is identical. An alternative of
// the errors.New function from github.com/pkg/errors.
func ( string) error {
	return &base{
		info:  ,
		stack: newStackTrace(),
		err:   nil,
	}
}

// Newf is like New, but it formats input according to a format specifier.
// An alternative of the fmt.Errorf function.
//
// If no args have been passed, it is same as `New` function without formatting. Character like
// '%' still has to be escaped in that scenario.
func ( string,  ...interface{}) error {
	return &base{
		info:  fmt.Sprintf(, ...),
		stack: newStackTrace(),
		err:   nil,
	}
}

// Wrap returns a new error, which wraps another error with a stacktrace containing recent call frames.
//
// If cause is nil, it does not produce the error, similar to errors.Wrap from github.com/pkg/errors was doing.
// Still, avoid `errors.Wrap(nil, "...") patterns as it can lead to inefficiencies while constructing arguments
// to format as well readability issues. We keep this behaviour to make sure it's a drop-in replacement.
func ( error,  string) error {
	if  == nil {
		return nil
	}

	return &base{
		info:  ,
		stack: newStackTrace(),
		err:   ,
	}
}

// Wrapf is like Wrap but the message is formatted with the supplied format specifier.
//
// If no args have been passed, it is same as `Wrap` function without formatting.
// Character like '%' still has to be escaped in that scenario.
func ( error,  string,  ...interface{}) error {
	if  == nil {
		return nil
	}
	return &base{
		info:  fmt.Sprintf(, ...),
		stack: newStackTrace(),
		err:   ,
	}
}

// Cause returns the result of repeatedly calling the Unwrap method on err, if err's
// type implements an Unwrap method. Otherwise, Cause returns the last encountered error.
// The difference between Unwrap and Cause is the first one performs unwrapping of one level
// but Cause returns the last err (whether it's nil or not) where it failed to assert
// the interface containing the Unwrap method.
// This is a replacement of errors.Cause without the causer interface from pkg/errors which
// actually can be sufficed through the errors.Is function. But considering some use cases
// where we need to peel off all the external layers applied through errors.Wrap family,
// it is useful ( where external SDK doesn't use errors.Is internally).
func ( error) error {
	for  != nil {
		,  := .(interface {
			() error
		})
		if ! {
			return 
		}
		 = .()
	}
	return nil
}

// formatErrorChain formats an error chain.
func formatErrorChain( error) string {
	var  strings.Builder
	for  != nil {
		if ,  := .(*base);  {
			.WriteString(fmt.Sprintf("%s\n%v", .info, .stack))
			 = .err
		} else {
			.WriteString(fmt.Sprintf("%s\n", .Error()))
			 = nil
		}
	}
	return .String()
}

// The functions `Is`, `As` & `Unwrap` provides a thin wrapper around the builtin errors
// package in go. Just for sake of completeness and correct autocompletion behaviors from
// IDEs they have been wrapped using functions instead of using variable to reference them
// as first class functions (eg: var Is = errros.Is ).

// Is is a wrapper of built-in errors.Is. It reports whether any error in err's
// chain matches target. The chain consists of err itself followed by the sequence
// of errors obtained by repeatedly calling Unwrap.
func (,  error) bool {
	return errors.Is(, )
}

// As is a wrapper of built-in errors.As. It finds the first error in err's
// chain that matches target, and if one is found, sets target to that error
// value and returns true. Otherwise, it returns false.
func ( error,  interface{}) bool {
	return errors.As(, )
}

// Unwrap is a wrapper of built-in errors.Unwrap. Unwrap returns the result of
// calling the Unwrap method on err, if err's type contains an Unwrap method
// returning error. Otherwise, Unwrap returns nil.
func ( error) error {
	return errors.Unwrap()
}