forked from oxequa/realize
/
errors.go
146 lines (125 loc) · 4.18 KB
/
errors.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package realize
import (
"fmt"
"runtime"
"strings"
"github.com/satori/go.uuid"
)
var (
// Prefix the error prefix, applies to each error's message.
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 NewError(errMsg string) Error {
id, _ := uuid.NewV4()
return Error{
ID: id.String(),
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
}
// 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.HasPrefix(message, "\\n") {
message = message[3 : len(message)-3]
} else if strings.HasPrefix(message, "\n") {
message = message[2 : len(message)-2]
}
if strings.HasSuffix(message, "\\n") {
return message[0 : len(message)-3]
} else if strings.HasSuffix(message, "\n") {
return message[0 : len(message)-2]
}
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, NewError(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())
}
// 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)
}