iris/core/errors/errors.go
Gerasimos (Makis) Maropoulos 07046ab978 formatting
Former-commit-id: 037081db5d6d4434e873ca8b75334ee43e046b6a
2019-08-17 10:06:20 +03:00

167 lines
4.6 KiB
Go

package errors
import (
"fmt"
"runtime"
"strings"
"github.com/iris-contrib/go.uuid"
)
// Prefix the error prefix, applies to each error's message.
var Prefix = ""
// Error holds the error message, this message never really changes
type Error struct {
// ID returns the unique id of the error, it's needed
// when we want to check if a specific error returned
// but the `Error() string` value is not the same because the error may be dynamic
// by a `Format` call.
ID string `json:"id"`
// The message of the error.
Message string `json:"message"`
// Apennded is true whenever it's a child error.
Appended bool `json:"appended"`
// Stack returns the list of the errors that are shown at `Error() string`.
Stack []Error `json:"stack"` // filled on AppendX.
}
// New creates and returns an Error with a pre-defined user output message
// all methods below that doesn't accept a pointer receiver because actually they are not changing the original message
func New(errMsg string) Error {
uidv4, _ := uuid.NewV4() // skip error.
return Error{
ID: uidv4.String(),
Message: Prefix + errMsg,
}
}
// NewFromErr same as `New` but pointer for nil checks without the need of the `Return()` function.
func NewFromErr(err error) *Error {
if err == nil {
return nil
}
errp := New(err.Error())
return &errp
}
// Equal returns true if "e" and "to" are matched, by their IDs if it's a core/errors type otherwise it tries to match their error messages.
// It will always returns true if the "to" is a children of "e"
// or the error messages are exactly the same, otherwise false.
func (e Error) Equal(to error) bool {
if e2, ok := to.(Error); ok {
return e.ID == e2.ID
} else if e2, ok := to.(*Error); ok {
return e.ID == e2.ID
}
return e.Error() == to.Error()
}
// Empty returns true if the "e" Error has no message on its stack.
func (e Error) Empty() bool {
return e.Message == ""
}
// NotEmpty returns true if the "e" Error has got a non-empty message on its stack.
func (e Error) NotEmpty() bool {
return !e.Empty()
}
// String returns the error message
func (e Error) String() string {
return e.Message
}
// Error returns the message of the actual error
// implements the error
func (e Error) Error() string {
return e.String()
}
// Format returns a formatted new error based on the arguments
// it does NOT change the original error's message
func (e Error) Format(a ...interface{}) Error {
e.Message = fmt.Sprintf(e.Message, a...)
return e
}
func omitNewLine(message string) string {
if strings.HasSuffix(message, "\n") {
return message[0 : len(message)-2]
} else if strings.HasSuffix(message, "\\n") {
return message[0 : len(message)-3]
}
return message
}
// AppendInline appends an error to the stack.
// It doesn't try to append a new line if needed.
func (e Error) AppendInline(format string, a ...interface{}) Error {
msg := fmt.Sprintf(format, a...)
e.Message += msg
e.Appended = true
e.Stack = append(e.Stack, New(omitNewLine(msg)))
return e
}
// Append adds a message to the predefined error message and returns a new error
// it does NOT change the original error's message
func (e Error) Append(format string, a ...interface{}) Error {
// if new line is false then append this error but first
// we need to add a new line to the first, if it was true then it has the newline already.
if e.Message != "" {
e.Message += "\n"
}
return e.AppendInline(format, a...)
}
// AppendErr adds an error's message to the predefined error message and returns a new error.
// it does NOT change the original error's message
func (e Error) AppendErr(err error) Error {
return e.Append(err.Error())
}
// HasStack returns true if the Error instance is created using Append/AppendInline/AppendErr funcs.
func (e Error) HasStack() bool {
return len(e.Stack) > 0
}
// With does the same thing as Format but it receives an error type which if it's nil it returns a nil error.
func (e Error) With(err error) error {
if err == nil {
return nil
}
return e.Format(err.Error())
}
// Ignore will ignore the "err" and return nil.
func (e Error) Ignore(err error) error {
if err == nil {
return e
}
if e.Error() == err.Error() {
return nil
}
return e
}
// Panic output the message and after panics.
func (e Error) Panic() {
_, fn, line, _ := runtime.Caller(1)
errMsg := e.Message
errMsg += "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
panic(errMsg)
}
// Panicf output the formatted message and after panics.
func (e Error) Panicf(args ...interface{}) {
_, fn, line, _ := runtime.Caller(1)
errMsg := e.Format(args...).Error()
errMsg += "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
panic(errMsg)
}