Created
December 12, 2023 10:54
-
-
Save kkharji/d2608e949a3c0b940cb70afdfbf741b5 to your computer and use it in GitHub Desktop.
Errx library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package to provide advanced error type with the purpose of making errors | |
// easy to read and trace, construct and pass, wrap and extend and more. | |
package errx | |
import ( | |
"errors" | |
"fmt" | |
"github.com/gofiber/fiber/v2" | |
"go.uber.org/zap/zapcore" | |
) | |
// A unique string describing a method or a function. | |
// Hint: Defined at the top of method or function | |
type Op = string | |
// Error Kind (what category is this error part of) | |
type Kind struct{ name string } | |
var ( | |
KindNotFound = &Kind{name: "Not Found"} | |
KindBadRequest = &Kind{name: "Bad Request"} | |
KindUnexpected = &Kind{name: "Unexpected"} | |
KindValidation = &Kind{name: "Validation"} | |
KindConflict = &Kind{name: "Conflict"} | |
) | |
var _ error = (*Error)(nil) | |
// Error Struct | |
type Error struct { | |
Op Op // Op is the operation resulted in the error | |
Kind *Kind // Kind is the kind of the error | |
Err error // Err is the wrapped error | |
Meta map[string]any // Meta is metadata associated with the error | |
Severity *zapcore.Level // Severity of the error (the lower, the more expected the error is) | |
} | |
const ( | |
SeverityDebug zapcore.Level = zapcore.DebugLevel | |
SeverityInfo = zapcore.InfoLevel | |
SeverityWarn = zapcore.WarnLevel | |
SeverityError = zapcore.ErrorLevel | |
SeverityPanic = zapcore.PanicLevel | |
SeverityFatal = zapcore.FatalLevel | |
) | |
// implements error interface | |
func (e *Error) Error() string { | |
if e.Err != nil { | |
subErrMsg := e.Err.Error() | |
if subErrMsg != "" { | |
return fmt.Sprintf("%s: %s", e.Op, e.Err.Error()) | |
} else { | |
return e.Op | |
} | |
} else { | |
return e.Op | |
} | |
} | |
// Ops returns the "stack" of operations for each generated error. | |
func Ops(e *Error) []Op { | |
operations := []Op{e.Op} | |
subErr, ok := e.Err.(*Error) | |
if !ok { | |
return operations | |
} | |
operations = append(operations, Ops(subErr)...) | |
return operations | |
} | |
// E create new Error with given parameters. Any order should be sufficient. | |
func E(parameters ...interface{}) error { | |
e := &Error{} | |
for _, param := range parameters { | |
switch param := param.(type) { | |
case Op: | |
e.Op = param | |
case error: | |
e.Err = param | |
case *Kind: | |
e.Kind = param | |
case *zapcore.Level: | |
e.Severity = param | |
case map[string]any: | |
e.Meta = param | |
} | |
} | |
// We want to try inherit kind or severity from wrapped error if not set | |
if e.Err != nil && (e.Kind == nil || e.Severity == nil) { | |
if subErr, ok := e.Err.(*Error); ok { | |
if e.Severity == nil { | |
e.Severity = subErr.Severity | |
} | |
if e.Kind == nil { | |
e.Kind = subErr.Kind | |
} | |
} | |
} | |
return e | |
} | |
// E create new Error with message and any parameters | |
func EMsg(msg string, parameters ...interface{}) error { | |
err := errors.New(msg) | |
parameters = append(parameters, err) | |
return E(parameters) | |
} | |
func IntoHTTPError(err error) error { | |
error, ok := err.(*Error) | |
if !ok { | |
return fiber.NewError(fiber.StatusInternalServerError, err.Error()) | |
} | |
code := fiber.StatusInternalServerError | |
switch error.Kind { | |
case KindNotFound: | |
code = fiber.StatusNotFound | |
case KindBadRequest: | |
code = fiber.StatusBadRequest | |
case KindUnexpected: | |
code = fiber.StatusInternalServerError | |
case KindValidation: | |
code = fiber.StatusBadRequest | |
case KindConflict: | |
code = fiber.StatusConflict | |
} | |
ops := Ops(error) | |
msg := string(ops[len(ops)-1]) | |
if msg == error.Op { | |
msg = error.Err.Error() | |
} | |
return fiber.NewError(code, msg) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Package to provide advanced error type with the purpose of making errors | |
// easy to read and trace, construct and pass, wrap and extend and more. | |
package errx | |
import ( | |
"testing" | |
"github.com/stretchr/testify/assert" | |
) | |
func TestE(t *testing.T) { | |
causeInner := E("invalid url") // a function | |
errorInner := E("db.connect", SeverityError, KindUnexpected, causeInner) // a function | |
errorOuter := E("users.get", errorInner) // finall function | |
assert.Equal(t, "users.get: db.connect: invalid url", errorOuter.Error()) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment