feat(errors): add errors package (#5741)

### Summary

Add errors package

#### Related Issues / PR's

https://github.com/SigNoz/signoz/pull/5710
This commit is contained in:
Vibhu Pandey 2024-08-22 15:19:32 +05:30 committed by GitHub
parent e7b5410c5b
commit c322fc72d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 206 additions and 0 deletions

3
pkg/errors/doc.go Normal file
View File

@ -0,0 +1,3 @@
// package error contains error related utilities. Use this package when
// a well-defined error has to be shown.
package errors

118
pkg/errors/errors.go Normal file
View File

@ -0,0 +1,118 @@
package errors
import (
"fmt"
)
// base is the fundamental struct that implements the error interface.
// The order of the struct is 'TCMEUA'.
type base struct {
// t denotes the custom type of the error.
t typ
// c denotes the short code for the error message.
c string
// m contains error message passed through errors.New.
m string
// e is the actual error being wrapped.
e error
// u denotes the url for the documentation (if present) for the error.
u string
// a denotes any additional error messages (if present).
a []string
}
// base implements Error interface.
func (b *base) Error() string {
if b.e != nil {
return b.e.Error()
}
return fmt.Sprintf("%s(%s): %s", b.t.s, b.c, b.m)
}
// New returns a base error. It requires type, code and message as input.
func New(t typ, code string, message string) *base {
return &base{
t: t,
c: code,
m: message,
e: nil,
u: "",
a: []string{},
}
}
// Newf returns a new base by formatting the error message with the supplied format specifier.
func Newf(t typ, code string, format string, args ...interface{}) *base {
return &base{
t: t,
c: code,
m: fmt.Sprintf(format, args...),
e: nil,
}
}
// Wrapf returns a new error by formatting the error message with the supplied format specifier
// and wrapping another error with base.
func Wrapf(cause error, t typ, code string, format string, args ...interface{}) *base {
return &base{
t: t,
c: code,
m: fmt.Sprintf(format, args...),
e: cause,
}
}
// WithUrl adds a url to the base error and returns a new base error.
func (b *base) WithUrl(u string) *base {
return &base{
t: b.t,
c: b.c,
m: b.m,
e: b.e,
u: u,
a: b.a,
}
}
// WithUrl adds additional messages to the base error and returns a new base error.
func (b *base) WithAdditional(a ...string) *base {
return &base{
t: b.t,
c: b.c,
m: b.m,
e: b.e,
u: b.u,
a: a,
}
}
// Unwrapb is a combination of built-in errors.As and type casting.
// It finds the first error in cause that matches base,
// and if one is found, returns the individual fields of base.
// Otherwise, it returns TypeInternal, the original error string
// and the error itself.
//
//lint:ignore ST1008 we want to return arguments in the 'TCMEUA' order of the struct
func Unwrapb(cause error) (typ, string, string, error, string, []string) {
base, ok := cause.(*base)
if ok {
return base.t, base.c, base.m, base.e, base.u, base.a
}
return TypeInternal, "", cause.Error(), cause, "", []string{}
}
// Ast checks if the provided error matches the specified custom error type.
func Ast(cause error, typ typ) bool {
t, _, _, _, _, _ := Unwrapb(cause)
return t == typ
}
// Ast checks if the provided error matches the specified custom error code.
func Asc(cause error, code string) bool {
_, c, _, _, _, _ := Unwrapb(cause)
return c == code
}

53
pkg/errors/errors_test.go Normal file
View File

@ -0,0 +1,53 @@
package errors
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestNew(t *testing.T) {
typ := typ{"test-error"}
err := New(typ, "code", "test error info")
assert.NotNil(t, err)
}
func TestNewf(t *testing.T) {
typ := typ{"test-error"}
err := Newf(typ, "test-code", "test error info with %s", "string")
assert.NotNil(t, err)
assert.Equal(t, "test-error(test-code): test error info with string", err.Error())
}
func TestWrapf(t *testing.T) {
typ := typ{"test-error"}
err := Wrapf(errors.New("original error"), typ, "test-code", "info for err %d", 2)
assert.NotNil(t, err)
}
func TestError(t *testing.T) {
typ := typ{"test-error"}
err1 := New(typ, "test-code", "info for err1")
assert.Equal(t, "test-error(test-code): info for err1", err1.Error())
err2 := Wrapf(err1, typ, "test-code", "info for err2")
assert.Equal(t, "test-error(test-code): info for err1", err2.Error())
}
func TestUnwrapb(t *testing.T) {
typ := typ{"test-error"}
oerr := errors.New("original error")
berr := Wrapf(oerr, typ, "test-code", "this is a base err").WithUrl("https://docs").WithAdditional("additional err")
atyp, acode, amessage, aerr, au, aa := Unwrapb(berr)
assert.Equal(t, typ, atyp)
assert.Equal(t, "test-code", acode)
assert.Equal(t, "this is a base err", amessage)
assert.Equal(t, oerr, aerr)
assert.Equal(t, "https://docs", au)
assert.Equal(t, []string{"additional err"}, aa)
atyp, _, _, _, _, _ = Unwrapb(oerr)
assert.Equal(t, TypeInternal, atyp)
}

14
pkg/errors/type.go Normal file
View File

@ -0,0 +1,14 @@
package errors
var (
TypeInvalidInput typ = typ{"invalid-input"}
TypeInternal = typ{"internal"}
TypeUnsupported = typ{"unsupported"}
TypeNotFound = typ{"not-found"}
TypeMethodNotAllowed = typ{"method-not-allowed"}
TypeAlreadyExists = typ{"already-exists"}
TypeUnauthenticated = typ{"unauthenticated"}
)
// Defines custom error types
type typ struct{ s string }

18
pkg/errors/type_test.go Normal file
View File

@ -0,0 +1,18 @@
package errors
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestString(t *testing.T) {
typ := typ{"test-error"}
assert.Equal(t, typ.s, "test-error")
}
func TestEquals(t *testing.T) {
typ1 := typ{"test-error"}
typ2 := typ{"test-error"}
assert.True(t, typ1 == typ2)
}