Initial commit
This commit is contained in:
114
errors/errors.go
Normal file
114
errors/errors.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type fatal interface {
|
||||
Fatal() bool
|
||||
}
|
||||
|
||||
func IsFatal(err error) bool {
|
||||
te, ok := errors.Unwrap(err).(fatal)
|
||||
return ok && te.Fatal()
|
||||
}
|
||||
|
||||
func As(err error, target any) bool {
|
||||
return errors.As(err, target)
|
||||
}
|
||||
|
||||
func Is(err, target error) bool {
|
||||
return errors.Is(err, target)
|
||||
}
|
||||
|
||||
func Unwrap(err error) error {
|
||||
return errors.Unwrap(err)
|
||||
}
|
||||
|
||||
type Error struct {
|
||||
Err error
|
||||
Stack string
|
||||
fatal bool
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("%v\n", e.Err))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *Error) ErrorWithStack() string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(fmt.Sprintf("%v\n", e.Err))
|
||||
sb.WriteString(fmt.Sprintf("Stack Trace:\n%s", e.Stack))
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (e *Error) Fatal() bool {
|
||||
return e.fatal
|
||||
}
|
||||
|
||||
func (e *Error) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func NewError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if it's already of our own
|
||||
// Error type, so we don't add stack twice.
|
||||
var asError *Error
|
||||
if errors.As(err, &asError) {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the stack trace
|
||||
var stack strings.Builder
|
||||
buf := make([]uintptr, 50)
|
||||
n := runtime.Callers(2, buf)
|
||||
frames := runtime.CallersFrames(buf[:n])
|
||||
|
||||
// Format the stack trace
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
// Skip runtime and standard library frames
|
||||
if !strings.Contains(frame.File, "runtime/") {
|
||||
stack.WriteString(fmt.Sprintf("\t%s:%d - %s\n", frame.File, frame.Line, frame.Function))
|
||||
}
|
||||
if !more {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return &Error{
|
||||
Err: err,
|
||||
Stack: stack.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func NewFatalError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if it's already of our own
|
||||
// Error type.
|
||||
var asError *Error
|
||||
if errors.As(err, &asError) {
|
||||
return err
|
||||
}
|
||||
err2 := NewError(err)
|
||||
err2.(*Error).fatal = true
|
||||
return err2
|
||||
}
|
||||
|
||||
var ConnectionError error = fmt.Errorf("connection error")
|
||||
|
||||
func NewConnectionError(err error) error {
|
||||
return fmt.Errorf("%w: %w", ConnectionError, err)
|
||||
}
|
||||
71
errors/errors_test.go
Normal file
71
errors/errors_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type CustomError struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *CustomError) Error() string { return e.Err.Error() }
|
||||
|
||||
func IsCustomError(err error) bool {
|
||||
var asError *CustomError
|
||||
return errors.As(err, &asError)
|
||||
}
|
||||
|
||||
func TestWrapping(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalErr := errors.New("original error")
|
||||
err1 := NewError(originalErr)
|
||||
if !errors.Is(err1, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
if !Is(err1, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
unwrappedErr := errors.Unwrap(err1)
|
||||
if !errors.Is(unwrappedErr, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
if !Is(unwrappedErr, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
unwrappedErr = Unwrap(err1)
|
||||
if !errors.Is(unwrappedErr, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
if !Is(unwrappedErr, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
wrappedErr := fmt.Errorf("wrapped: %w", originalErr)
|
||||
if !errors.Is(wrappedErr, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
if !Is(wrappedErr, originalErr) {
|
||||
t.Errorf("original error is not wrapped")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewError(t *testing.T) {
|
||||
t.Parallel()
|
||||
originalErr := &CustomError{errors.New("err1")}
|
||||
if !IsCustomError(originalErr) {
|
||||
t.Errorf("TestNewError fail #1")
|
||||
}
|
||||
err1 := NewError(originalErr)
|
||||
if !IsCustomError(err1) {
|
||||
t.Errorf("TestNewError fail #2")
|
||||
}
|
||||
wrappedErr1 := fmt.Errorf("wrapped %w", err1)
|
||||
if !IsCustomError(wrappedErr1) {
|
||||
t.Errorf("TestNewError fail #3")
|
||||
}
|
||||
unwrappedErr1 := Unwrap(wrappedErr1)
|
||||
if !IsCustomError(unwrappedErr1) {
|
||||
t.Errorf("TestNewError fail #4")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user