feat: Initial
This commit is contained in:
commit
f0abca60db
26 files changed
+1112
No files matched your search
@@ -0,0 +1,69 @@
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type UniverseErrorBuilder struct {
|
||||
t *UniverseErrorType
|
||||
message string
|
||||
cause error
|
||||
fields map[interface{}]any
|
||||
stack ErrorStack
|
||||
}
|
||||
|
||||
func Builder() *UniverseErrorBuilder {
|
||||
return &UniverseErrorBuilder{
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func BuilderEmpty() *UniverseErrorBuilder {
|
||||
return &UniverseErrorBuilder{}
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) Message(message string) *UniverseErrorBuilder {
|
||||
b.message = message
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) Messagef(format string, args ...interface{}) *UniverseErrorBuilder {
|
||||
b.message = fmt.Sprintf(format, args...)
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) Type(typeName *UniverseErrorType) *UniverseErrorBuilder {
|
||||
b.t = typeName
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) Cause(cause error) *UniverseErrorBuilder {
|
||||
b.cause = cause
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) CaptureStackIfEnabled() *UniverseErrorBuilder {
|
||||
b.stack = CaptureStackIfEnabled()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) CaptureStack() *UniverseErrorBuilder {
|
||||
b.stack = CaptureStack()
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) WithField(key interface{}, value interface{}) *UniverseErrorBuilder {
|
||||
if b.fields == nil {
|
||||
b.fields = make(map[interface{}]any)
|
||||
}
|
||||
b.fields[key] = value
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *UniverseErrorBuilder) Build() UniverseError {
|
||||
return &UniverseErrorImpl{
|
||||
msg: b.message,
|
||||
cause: b.cause,
|
||||
fields: b.fields,
|
||||
stack: b.stack,
|
||||
t: b.t,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package errors
|
||||
|
||||
type UniverseError interface {
|
||||
Error() string
|
||||
|
||||
Type() *UniverseErrorType
|
||||
Message() string
|
||||
Cause() error
|
||||
StackTrace() ErrorStack
|
||||
|
||||
GetAny(key interface{}) any
|
||||
With(key interface{}, value any) UniverseError
|
||||
Builder() *UniverseErrorBuilder
|
||||
}
|
||||
|
||||
type UniverseErrorImpl struct {
|
||||
msg string
|
||||
cause error
|
||||
t *UniverseErrorType
|
||||
stack ErrorStack
|
||||
fields map[interface{}]any
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) Type() *UniverseErrorType {
|
||||
return e.t
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) StackTrace() ErrorStack {
|
||||
return e.stack
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) With(field interface{}, value any) UniverseError {
|
||||
if e.fields == nil {
|
||||
e.fields = map[interface{}]any{}
|
||||
}
|
||||
e.fields[field] = value
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) Builder() *UniverseErrorBuilder {
|
||||
return &UniverseErrorBuilder{
|
||||
t: e.t,
|
||||
message: e.msg,
|
||||
cause: e.cause,
|
||||
fields: e.fields,
|
||||
stack: e.stack,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) Error() string {
|
||||
return ErrorToString(e)
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) Message() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
func (e *UniverseErrorImpl) GetAny(field interface{}) any {
|
||||
if e.fields == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return e.fields[field]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ErrorToString(e error) string {
|
||||
builder := &strings.Builder{}
|
||||
AppendError(builder, e, 0)
|
||||
return builder.String()
|
||||
}
|
||||
|
||||
func AppendError(writer io.StringWriter, err error, depth uint) {
|
||||
padding := strings.Repeat(" ", int(depth))
|
||||
|
||||
var uniError UniverseError
|
||||
if As(err, &uniError) {
|
||||
errorType := uniError.Type()
|
||||
if errorType != nil {
|
||||
_, _ = writer.WriteString(errorType.Name)
|
||||
_, _ = writer.WriteString(": ")
|
||||
}
|
||||
|
||||
_, _ = writer.WriteString(uniError.Message())
|
||||
|
||||
stack := uniError.StackTrace()
|
||||
if stack != nil {
|
||||
for _, value := range stack {
|
||||
_, _ = writer.WriteString("\n")
|
||||
_, _ = writer.WriteString(padding)
|
||||
_, _ = writer.WriteString(value)
|
||||
}
|
||||
}
|
||||
|
||||
cause := uniError.Cause()
|
||||
if cause != nil {
|
||||
_, _ = writer.WriteString("\n")
|
||||
_, _ = writer.WriteString(padding)
|
||||
_, _ = writer.WriteString("Caused by: ")
|
||||
AppendError(writer, cause, depth+1)
|
||||
}
|
||||
|
||||
} else {
|
||||
_, _ = writer.WriteString(err.Error())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
IsStackCaptureEnabled = true
|
||||
StackCaptureSize = 1024 * 4
|
||||
)
|
||||
|
||||
type ErrorStack = []string
|
||||
|
||||
func CaptureStackIfEnabled() ErrorStack {
|
||||
if !IsStackCaptureEnabled {
|
||||
return nil
|
||||
}
|
||||
|
||||
return CaptureStack()
|
||||
}
|
||||
|
||||
func CaptureStack() ErrorStack {
|
||||
buffer := make([]byte, StackCaptureSize)
|
||||
stackSize := runtime.Stack(buffer, false)
|
||||
return strings.Split(string(buffer[:stackSize]), "\n")
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
type UniverseErrorType struct {
|
||||
error
|
||||
|
||||
ParentType *UniverseErrorType
|
||||
Name string
|
||||
DefaultMessage string
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) Builder() *UniverseErrorBuilder {
|
||||
return &UniverseErrorBuilder{
|
||||
t: e,
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) CreateWithCause(cause error) UniverseError {
|
||||
return &UniverseErrorImpl{
|
||||
t: e,
|
||||
cause: cause,
|
||||
msg: e.DefaultMessage,
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) CreateWithMessage(message string) UniverseError {
|
||||
return &UniverseErrorImpl{
|
||||
t: e,
|
||||
msg: message,
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) Create(message string, cause error) UniverseError {
|
||||
return &UniverseErrorImpl{
|
||||
t: e,
|
||||
msg: message,
|
||||
cause: cause,
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) CreateEmpty() UniverseError {
|
||||
return &UniverseErrorImpl{
|
||||
t: e,
|
||||
msg: e.DefaultMessage,
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) Is(err error) bool {
|
||||
return Is(err, e)
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) Child(childName string) *UniverseErrorType {
|
||||
return &UniverseErrorType{
|
||||
ParentType: e,
|
||||
Name: childName,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) Error() string {
|
||||
return fmt.Sprintf("type: %s", e.Name)
|
||||
}
|
||||
|
||||
func (e *UniverseErrorType) Panic() {
|
||||
panic(e.CreateEmpty())
|
||||
}
|
||||
|
||||
func Type(name string, defaultMessage string) *UniverseErrorType {
|
||||
return &UniverseErrorType{
|
||||
Name: name,
|
||||
DefaultMessage: defaultMessage,
|
||||
}
|
||||
}
|
||||
|
||||
func New(message string) *UniverseErrorType {
|
||||
return &UniverseErrorType{
|
||||
DefaultMessage: message,
|
||||
}
|
||||
}
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func Wrap(err error, message string) error {
|
||||
return &UniverseErrorImpl{
|
||||
msg: message,
|
||||
cause: err,
|
||||
stack: CaptureStackIfEnabled(),
|
||||
}
|
||||
}
|
||||
|
||||
func Get[T *any](err error, field string) T {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var uerr UniverseError
|
||||
if As(err, &uerr) {
|
||||
value := uerr.GetAny(field)
|
||||
if value, ok := value.(T); ok {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Unwrap(err error) error {
|
||||
u, ok := err.(interface {
|
||||
Unwrap() error
|
||||
})
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return u.Unwrap()
|
||||
}
|
||||
|
||||
func Is(err, target error) bool {
|
||||
if err == nil || target == nil {
|
||||
return err == target
|
||||
}
|
||||
|
||||
isComparable := reflect.TypeOf(target).Comparable()
|
||||
return is(err, target, isComparable)
|
||||
}
|
||||
|
||||
func is(err, target error, targetComparable bool) bool {
|
||||
for {
|
||||
if targetComparable && err == target {
|
||||
return true
|
||||
}
|
||||
if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
|
||||
return true
|
||||
}
|
||||
switch x := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = x.Unwrap()
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
case interface{ Unwrap() []error }:
|
||||
for _, err := range x.Unwrap() {
|
||||
if is(err, target, targetComparable) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func As(err error, target any) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if target == nil {
|
||||
panic("errors: target cannot be nil")
|
||||
}
|
||||
val := reflect.ValueOf(target)
|
||||
typ := val.Type()
|
||||
if typ.Kind() != reflect.Ptr || val.IsNil() {
|
||||
panic("errors: target must be a non-nil pointer")
|
||||
}
|
||||
targetType := typ.Elem()
|
||||
if targetType.Kind() != reflect.Interface && !targetType.Implements(errorType) {
|
||||
panic("errors: *target must be interface or implement error")
|
||||
}
|
||||
return as(err, target, val, targetType)
|
||||
}
|
||||
|
||||
func as(err error, target any, targetVal reflect.Value, targetType reflect.Type) bool {
|
||||
for {
|
||||
if reflect.TypeOf(err).AssignableTo(targetType) {
|
||||
targetVal.Elem().Set(reflect.ValueOf(err))
|
||||
return true
|
||||
}
|
||||
if x, ok := err.(interface{ As(any) bool }); ok && x.As(target) {
|
||||
return true
|
||||
}
|
||||
switch x := err.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
err = x.Unwrap()
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
case interface{ Unwrap() []error }:
|
||||
for _, err := range x.Unwrap() {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
if as(err, target, targetVal, targetType) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
Reference in new issue
Block a user