feat: Initial

This commit is contained in:
Artёm Mishin committed 2025-04-25 11:20:31 +03:00
commit f0abca60db
26 files changed
+1112

No files matched your search

+69
View File
@@ -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,
}
}
+72
View File
@@ -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]
}
+47
View File
@@ -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())
}
}
+27
View File
@@ -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")
}
+84
View File
@@ -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
View File
@@ -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()