mirror of
https://github.com/kataras/iris.git
synced 2026-01-08 20:41:57 +00:00
Add notes for the new lead maintainer of the open-source iris project and align with @get-ion/ion by @hiveminded
Former-commit-id: da4f38eb9034daa49446df3ee529423b98f9b331
This commit is contained in:
@@ -1,40 +1,62 @@
|
||||
// Copyright 2017 Gerasimos Maropoulos, ΓΜ. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
// Prefix the error prefix, applies to each error's message.
|
||||
Prefix = ""
|
||||
// NewLine adds a new line to the end of each error's message
|
||||
// defaults to true
|
||||
NewLine = true
|
||||
)
|
||||
|
||||
// Error holds the error message, this message never really changes
|
||||
type Error struct {
|
||||
message string
|
||||
appended bool
|
||||
// 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 {
|
||||
if NewLine {
|
||||
errMsg += "\n"
|
||||
func New(errMsg string) Error {
|
||||
return Error{
|
||||
ID: uuid.NewV4().String(),
|
||||
Message: Prefix + errMsg,
|
||||
}
|
||||
return &Error{message: Prefix + errMsg}
|
||||
}
|
||||
|
||||
// Equal returns true if "e" and "e2" are matched, by their IDs.
|
||||
// It will always returns true if the "e2" is a children of "e"
|
||||
// or the error messages are exactly the same, otherwise false.
|
||||
func (e Error) Equal(e2 Error) bool {
|
||||
return e.ID == e2.ID || e.Error() == e2.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
|
||||
return e.Message
|
||||
}
|
||||
|
||||
// Error returns the message of the actual error
|
||||
@@ -46,34 +68,53 @@ func (e Error) Error() 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...)
|
||||
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 {
|
||||
// eCp := *e
|
||||
if NewLine {
|
||||
format += "\n"
|
||||
// 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"
|
||||
}
|
||||
e.message += fmt.Sprintf(format, a...)
|
||||
e.appended = true
|
||||
return e
|
||||
|
||||
return e.AppendInline(format, a...)
|
||||
}
|
||||
|
||||
// AppendErr adds an error's message to the predefined error message and returns a new error
|
||||
// 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())
|
||||
}
|
||||
|
||||
// IsAppended returns true if the Error instance is created using original's Error.Append/AppendErr func
|
||||
func (e Error) IsAppended() bool {
|
||||
return e.appended
|
||||
// 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
|
||||
// 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
|
||||
@@ -82,15 +123,15 @@ func (e Error) With(err error) error {
|
||||
return e.Format(err.Error())
|
||||
}
|
||||
|
||||
// Panic output the message and after panics
|
||||
// Panic output the message and after panics.
|
||||
func (e Error) Panic() {
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
errMsg := e.message
|
||||
errMsg := e.Message
|
||||
errMsg += "\nCaller was: " + fmt.Sprintf("%s:%d", fn, line)
|
||||
panic(errMsg)
|
||||
}
|
||||
|
||||
// Panicf output the formatted message and after panics
|
||||
// Panicf output the formatted message and after panics.
|
||||
func (e Error) Panicf(args ...interface{}) {
|
||||
_, fn, line, _ := runtime.Caller(1)
|
||||
errMsg := e.Format(args...).Error()
|
||||
|
||||
@@ -13,26 +13,16 @@ var errUserAlreadyExists = errors.New(errMessage)
|
||||
var userMail = "user1@mail.go"
|
||||
var expectedUserAlreadyExists = "User with mail: user1@mail.go already exists"
|
||||
|
||||
func getNewLine() string {
|
||||
if errors.NewLine {
|
||||
return "\n"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ExampleError() {
|
||||
fmt.Print(errUserAlreadyExists.Format(userMail))
|
||||
// first output first Output line
|
||||
|
||||
fmt.Print(errUserAlreadyExists.Format(userMail).Append("Please change your mail addr"))
|
||||
// second output second and third Output lines
|
||||
|
||||
// Output:
|
||||
// User with mail: user1@mail.go already exists
|
||||
// User with mail: user1@mail.go already exists
|
||||
// Please change your mail addr
|
||||
}
|
||||
|
||||
func do(method string, testErr *errors.Error, expectingMsg string, t *testing.T) {
|
||||
func do(method string, testErr errors.Error, expectingMsg string, t *testing.T) {
|
||||
formattedErr := func() error {
|
||||
return testErr.Format(userMail)
|
||||
}()
|
||||
@@ -43,56 +33,51 @@ func do(method string, testErr *errors.Error, expectingMsg string, t *testing.T)
|
||||
}
|
||||
|
||||
func TestFormat(t *testing.T) {
|
||||
expected := errors.Prefix + expectedUserAlreadyExists + getNewLine()
|
||||
expected := errors.Prefix + expectedUserAlreadyExists
|
||||
do("Format Test", errUserAlreadyExists, expected, t)
|
||||
}
|
||||
|
||||
func TestAppendErr(t *testing.T) {
|
||||
errors.NewLine = true
|
||||
errors.Prefix = "error: "
|
||||
|
||||
errChangeMailMsg := "Please change your mail addr"
|
||||
errChangeMail := fmt.Errorf(errChangeMailMsg) // test go standard error
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + errChangeMailMsg + getNewLine() // first Prefix and last newline lives inside do
|
||||
errChangeMail := fmt.Errorf(errChangeMailMsg) // test go standard error
|
||||
errAppended := errUserAlreadyExists.AppendErr(errChangeMail)
|
||||
do("Append Test Standard error type", &errAppended, expectedErrorMessage, t)
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMailMsg
|
||||
|
||||
do("Append Test Standard error type", errAppended, expectedErrorMessage, t)
|
||||
}
|
||||
|
||||
func TestAppendError(t *testing.T) {
|
||||
errors.NewLine = true
|
||||
errors.Prefix = "error: "
|
||||
|
||||
errChangeMailMsg := "Please change your mail addr"
|
||||
errChangeMail := errors.New(errChangeMailMsg) // test Error struct
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + errChangeMail.Error() + getNewLine() // first Prefix and last newline lives inside do
|
||||
errChangeMail := errors.New(errChangeMailMsg)
|
||||
|
||||
errAppended := errUserAlreadyExists.AppendErr(errChangeMail)
|
||||
do("Append Test Error type", &errAppended, expectedErrorMessage, t)
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMail.Error()
|
||||
|
||||
do("Append Test Error type", errAppended, expectedErrorMessage, t)
|
||||
}
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
errors.NewLine = true
|
||||
errors.Prefix = "error: "
|
||||
|
||||
errChangeMailMsg := "Please change your mail addr"
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + errChangeMailMsg + getNewLine() // first Prefix and last newline lives inside do
|
||||
expectedErrorMessage := errUserAlreadyExists.Format(userMail).Error() + "\n" + errChangeMailMsg
|
||||
errAppended := errUserAlreadyExists.Append(errChangeMailMsg)
|
||||
do("Append Test string Message", &errAppended, expectedErrorMessage, t)
|
||||
do("Append Test string Message", errAppended, expectedErrorMessage, t)
|
||||
}
|
||||
|
||||
func TestNewLine(t *testing.T) {
|
||||
errors.NewLine = false
|
||||
|
||||
errNoNewLine := errors.New(errMessage)
|
||||
err := errors.New(errMessage)
|
||||
expected := errors.Prefix + expectedUserAlreadyExists
|
||||
do("NewLine Test", errNoNewLine, expected, t)
|
||||
|
||||
errors.NewLine = true
|
||||
do("NewLine Test", err, expected, t)
|
||||
}
|
||||
|
||||
func TestPrefix(t *testing.T) {
|
||||
errors.Prefix = "MyPrefix: "
|
||||
|
||||
errUpdatedPrefix := errors.New(errMessage)
|
||||
expected := errors.Prefix + expectedUserAlreadyExists + "\n"
|
||||
expected := errors.Prefix + expectedUserAlreadyExists
|
||||
do("Prefix Test with "+errors.Prefix, errUpdatedPrefix, expected, t)
|
||||
}
|
||||
|
||||
141
core/errors/reporter.go
Normal file
141
core/errors/reporter.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// StackError contains the Stack method.
|
||||
type StackError interface {
|
||||
Stack() []Error
|
||||
Error() string
|
||||
}
|
||||
|
||||
// PrintAndReturnErrors prints the "err" to the given "printer",
|
||||
// printer will be called multiple times if the "err" is a StackError, where it contains more than one error.
|
||||
func PrintAndReturnErrors(err error, printer func(string, ...interface{})) error {
|
||||
if err == nil || err.Error() == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
if len(stackErr.Stack()) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
stack := stackErr.Stack()
|
||||
|
||||
for _, e := range stack {
|
||||
if e.HasStack() {
|
||||
for _, es := range e.Stack {
|
||||
printer("%v", es)
|
||||
}
|
||||
continue
|
||||
}
|
||||
printer("%v", e)
|
||||
}
|
||||
|
||||
return stackErr
|
||||
}
|
||||
|
||||
printer("%v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Reporter is a helper structure which can
|
||||
// stack errors and prints them to a printer of func(string).
|
||||
type Reporter struct {
|
||||
mu sync.Mutex
|
||||
wrapper Error
|
||||
}
|
||||
|
||||
// NewReporter returns a new empty error reporter.
|
||||
func NewReporter() *Reporter {
|
||||
return &Reporter{wrapper: New("")}
|
||||
}
|
||||
|
||||
// AddErr adds an error to the error stack.
|
||||
// if "err" is a StackError then
|
||||
// each of these errors will be printed as individual.
|
||||
func (r *Reporter) AddErr(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
r.addStack(stackErr.Stack())
|
||||
return
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.wrapper = r.wrapper.AppendErr(err)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// Add adds a formatted message as an error to the error stack.
|
||||
func (r *Reporter) Add(format string, a ...interface{}) {
|
||||
// usually used as: "module: %v", err so
|
||||
// check if the first argument is error and if that error is empty then don't add it.
|
||||
if len(a) > 0 {
|
||||
f := a[0]
|
||||
if e, ok := f.(interface {
|
||||
Error() string
|
||||
}); ok {
|
||||
if e.Error() == "" {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r.mu.Lock()
|
||||
r.wrapper = r.wrapper.Append(format, a...)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
// Describe same as `Add` but if "err" is nil then it does nothing.
|
||||
func (r *Reporter) Describe(format string, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if stackErr, ok := err.(StackError); ok {
|
||||
r.addStack(stackErr.Stack())
|
||||
return
|
||||
}
|
||||
|
||||
r.Add(format, err)
|
||||
}
|
||||
|
||||
// PrintStack prints all the errors to the given "printer".
|
||||
// Returns itself in order to be used as printer and return the full error in the same time.
|
||||
func (r Reporter) PrintStack(printer func(string, ...interface{})) error {
|
||||
return PrintAndReturnErrors(r, printer)
|
||||
}
|
||||
|
||||
// Stack returns the list of the errors in the stack.
|
||||
func (r Reporter) Stack() []Error {
|
||||
return r.wrapper.Stack
|
||||
}
|
||||
|
||||
func (r *Reporter) addStack(stack []Error) {
|
||||
for _, e := range stack {
|
||||
if e.Error() == "" {
|
||||
continue
|
||||
}
|
||||
r.mu.Lock()
|
||||
r.wrapper = r.wrapper.AppendErr(e)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// Error implements the error, returns the full error string.
|
||||
func (r Reporter) Error() string {
|
||||
return r.wrapper.Error()
|
||||
}
|
||||
|
||||
// Return returns nil if the error is empty, otherwise returns the full error.
|
||||
func (r Reporter) Return() error {
|
||||
if r.Error() == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
26
core/errors/reporter_test.go
Normal file
26
core/errors/reporter_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// black-box testing
|
||||
package errors_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/kataras/iris/core/errors"
|
||||
)
|
||||
|
||||
func TestReporterAdd(t *testing.T) {
|
||||
errors.Prefix = ""
|
||||
|
||||
r := errors.NewReporter()
|
||||
|
||||
tests := []string{"err1", "err3", "err4\nerr5"}
|
||||
for _, tt := range tests {
|
||||
r.Add(tt)
|
||||
}
|
||||
|
||||
for i, e := range r.Stack() {
|
||||
tt := tests[i]
|
||||
if expected, got := tt, e.Error(); expected != got {
|
||||
t.Fatalf("[%d] expected %s but got %s", i, expected, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user