summaryrefslogtreecommitdiff
path: root/errors/errors.go
diff options
context:
space:
mode:
Diffstat (limited to 'errors/errors.go')
-rwxr-xr-xerrors/errors.go219
1 files changed, 219 insertions, 0 deletions
diff --git a/errors/errors.go b/errors/errors.go
new file mode 100755
index 00000000..def408d8
--- /dev/null
+++ b/errors/errors.go
@@ -0,0 +1,219 @@
+package errors
+
+import (
+ "bytes"
+ "encoding"
+ "errors"
+ "fmt"
+ "log"
+ "runtime"
+)
+
+type Error struct {
+ Op Op
+ Kind Kind
+ Err error
+
+ // Stack information
+ stack
+}
+
+func (e *Error) isZero() bool {
+ return e.Op == "" && e.Kind == 0 && e.Err == nil
+}
+
+var (
+ _ error = (*Error)(nil)
+ _ encoding.BinaryUnmarshaler = (*Error)(nil)
+ _ encoding.BinaryMarshaler = (*Error)(nil)
+)
+
+// Op describes an operation
+type Op string
+
+// separator -> new line plus tabulator to intend error if previuos not nil
+var Separator = ":\n\t"
+
+type Kind uint8
+
+// Kinds of errors.
+const (
+ Undefined Kind = iota // Undefined error.
+ Network
+ Other
+ Test
+)
+
+func (k Kind) String() string {
+ switch k {
+ case Undefined:
+ return "UNDEF"
+ case Network:
+ return "Network error"
+ case Other:
+ return "Other"
+ case Test:
+ return "Test"
+
+ }
+ return "unknown error kind"
+}
+
+// E builds an error value from its arguments.
+func E(args ...interface{}) error {
+ e := &Error{}
+ if len(args) == 0 {
+ msg := "errors.E called with 0 args"
+ _, file, line, ok := runtime.Caller(1)
+ if ok {
+ msg = fmt.Sprintf("%v - %v:%v", msg, file, line)
+ }
+ e.Err = errors.New(msg)
+ }
+
+ for _, arg := range args {
+ switch arg := arg.(type) {
+ case Op:
+ e.Op = arg
+ case string:
+ e.Err = Str(arg)
+ case Kind:
+ e.Kind = arg
+ case *Error:
+ // Make a copy
+ eCopy := *arg
+ e.Err = &eCopy
+ case error:
+ e.Err = arg
+ // add map map[string]string
+ default:
+ _, file, line, _ := runtime.Caller(1)
+ log.Printf("errors.E: bad call from %s:%d: %v", file, line, args)
+ return Errorf("unknown type %T, value %v in error call", arg, arg)
+ }
+ }
+
+ // Populate stack information
+ e.populateStack()
+
+ prev, ok := e.Err.(*Error)
+ if !ok {
+ return e
+ }
+
+ if prev.Kind == e.Kind {
+ prev.Kind = Undefined
+ }
+
+ if e.Kind == Undefined {
+ e.Kind = prev.Kind
+ prev.Kind = Undefined
+ }
+ return e
+}
+
+func (e *Error) Error() string {
+ b := new(bytes.Buffer)
+ e.printStack(b)
+ if e.Op != "" {
+ appendStrToBuf(b, ": ")
+ b.WriteString(string(e.Op))
+ }
+
+ if e.Kind != 0 {
+ appendStrToBuf(b, ": ")
+ b.WriteString(e.Kind.String())
+ }
+ if e.Err != nil {
+ if prevErr, ok := e.Err.(*Error); ok {
+ if !prevErr.isZero() {
+ // indent - separator
+ appendStrToBuf(b, Separator)
+ b.WriteString(e.Err.Error())
+ }
+ } else {
+ appendStrToBuf(b, ": ")
+ b.WriteString(e.Err.Error())
+ }
+ }
+ if b.Len() == 0 {
+ return "no error"
+ }
+ return b.String()
+}
+
+// errors.New
+func Str(text string) error {
+ return &errorString{text}
+}
+
+type errorString struct {
+ s string
+}
+
+func (e *errorString) Error() string {
+ return e.s
+}
+
+func Errorf(format string, args ...interface{}) error {
+ return &errorString{fmt.Sprintf(format, args...)}
+}
+
+func Match(err1, err2 error) bool {
+ e1, ok := err1.(*Error)
+ if !ok {
+ return false
+ }
+ e2, ok := err2.(*Error)
+ if !ok {
+ return false
+ }
+ if e1.Op != "" && e2.Op != e1.Op {
+ return false
+ }
+ if e1.Kind != Undefined && e2.Kind != e1.Kind {
+ return false
+ }
+ if e1.Err != nil {
+ if _, ok := e1.Err.(*Error); ok {
+ return Match(e1.Err, e2.Err)
+ }
+ if e2.Err == nil || e2.Err.Error() != e1.Err.Error() {
+ return false
+ }
+ }
+ return true
+}
+
+// Is reports whether err is an *Error of the given Kind
+func Is(kind Kind, err error) bool {
+ e, ok := err.(*Error)
+ if !ok {
+ return false
+ }
+ if e.Kind != Undefined {
+ return e.Kind == kind
+ }
+ if e.Err != nil {
+ return Is(kind, e.Err)
+ }
+ return false
+}
+
+// Do smt with no care about result (and panics)
+func SafelyDo(work func()) {
+ defer func() {
+ if err := recover(); err != nil {
+ log.Printf("work failed: %s", err)
+ }
+ }()
+
+ work()
+}
+
+func appendStrToBuf(b *bytes.Buffer, str string) {
+ if b.Len() == 0 {
+ return
+ }
+ b.WriteString(str)
+}