213 lines
4.3 KiB
Go
213 lines
4.3 KiB
Go
package zerr
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"log/slog"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"connectrpc.com/connect"
|
|
"gitlab.bareksa.com/backend/zen/core/zoptions"
|
|
)
|
|
|
|
type Err struct {
|
|
message string
|
|
publicMessage string
|
|
code connect.Code
|
|
errs []error
|
|
caller uintptr
|
|
details []any
|
|
time time.Time
|
|
id string
|
|
logger Logger
|
|
notifier Notifier
|
|
sequence *Sequence
|
|
}
|
|
|
|
func (mu *Err) Error() string {
|
|
if len(mu.errs) == 0 {
|
|
return "[nil]"
|
|
}
|
|
s := strings.Builder{}
|
|
if len(mu.errs) == 1 {
|
|
str := getNestedErrorString(mu.errs[0])
|
|
str, found := strings.CutPrefix(str, mu.message)
|
|
if found {
|
|
str, _ = strings.CutPrefix(str, ": ")
|
|
}
|
|
s.WriteString(mu.message)
|
|
s.WriteString(": ")
|
|
s.WriteString(str)
|
|
return s.String()
|
|
}
|
|
s.WriteString(mu.message)
|
|
for i, e := range mu.errs {
|
|
if i > 0 {
|
|
s.WriteString("\n")
|
|
}
|
|
s.WriteString(strconv.Itoa(i + 1))
|
|
s.WriteString(". ")
|
|
s.WriteString(getNestedErrorString(e))
|
|
}
|
|
return s.String()
|
|
}
|
|
|
|
func getNestedErrorString(err error) string {
|
|
if err == nil {
|
|
return "[nil]"
|
|
}
|
|
s := strings.Builder{}
|
|
if e, ok := err.(interface{ Unwrap() error }); ok {
|
|
current := err.Error()
|
|
if err := e.Unwrap(); err != nil {
|
|
next := getNestedErrorString(err)
|
|
hasPrefix := strings.HasPrefix(next, current)
|
|
if !hasPrefix {
|
|
s.WriteString(current)
|
|
s.WriteString(": ")
|
|
}
|
|
s.WriteString(next)
|
|
}
|
|
}
|
|
|
|
if errs, ok := err.(interface{ Unwrap() []error }); ok {
|
|
for i, e := range errs.Unwrap() {
|
|
if i > 0 {
|
|
s.WriteString("\n")
|
|
}
|
|
s.WriteString(strconv.Itoa(i + 1))
|
|
s.WriteString(". ")
|
|
s.WriteString(getNestedErrorString(e))
|
|
}
|
|
}
|
|
|
|
if s.Len() == 0 {
|
|
s.WriteString(err.Error())
|
|
}
|
|
|
|
return s.String()
|
|
}
|
|
|
|
// LogValue returns log fields to be consumed by logger.
|
|
func (mu *Err) LogValue() slog.Value {
|
|
attrs := make([]slog.Attr, 0, len(mu.errs))
|
|
i := 0
|
|
for _, err := range mu.errs {
|
|
if err == nil {
|
|
continue
|
|
}
|
|
i++
|
|
attrs = append(attrs, slog.Attr{
|
|
Key: strconv.Itoa(i),
|
|
Value: errorValuer{err}.LogValue(),
|
|
})
|
|
}
|
|
return slog.GroupValue(attrs...)
|
|
}
|
|
|
|
// Code sets error code. Ignored in Multi.
|
|
func (mu *Err) Code(code connect.Code) Error {
|
|
mu.code = code
|
|
return mu
|
|
}
|
|
|
|
// GetCode Always returns 0 in Multi.
|
|
func (mu *Err) GetCode() connect.Code {
|
|
return mu.code
|
|
}
|
|
|
|
// Message sets error message. It uses fmt.Sprintf to format the message.
|
|
func (mu *Err) Message(msg string, args ...any) Error {
|
|
mu.message = fmt.Sprintf(msg, args...)
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) GetMessage() string {
|
|
return mu.message
|
|
}
|
|
|
|
func (mu *Err) PublicMessage(msg string, args ...any) Error {
|
|
mu.publicMessage = fmt.Sprintf(msg, args...)
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) GetPublicMessage() string {
|
|
return mu.publicMessage
|
|
}
|
|
|
|
func (mu *Err) Caller(pc uintptr) Error {
|
|
mu.caller = pc
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) GetCaller() uintptr {
|
|
return mu.caller
|
|
}
|
|
|
|
// Details sets error details. It's a key-value pair where the odd index is the key and the even index is the value.
|
|
//
|
|
// Invalid key-value pair number and format will cause the misplaced value to be paired up with "!BADKEY".
|
|
//
|
|
// Example:
|
|
//
|
|
// err.Details(
|
|
// "key1", 12345,
|
|
// "key2", float64(12345.67),
|
|
// )
|
|
func (mu *Err) Details(fields ...any) Error {
|
|
mu.details = fields
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) GetDetails() []any {
|
|
return mu.details
|
|
}
|
|
|
|
// Time sets the error time.
|
|
//
|
|
// Time is already set to current when the error is created or wrapped,
|
|
// so this method is only useful when you want to set a different time.
|
|
func (mu *Err) Time(t time.Time) Error {
|
|
mu.time = t
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) GetTime() time.Time {
|
|
return mu.time
|
|
}
|
|
|
|
// ID sets the error ID.
|
|
//
|
|
// ID is used to identify the error. Used by Zen to consider if an error is the same as another.
|
|
//
|
|
// Current implementation
|
|
func (mu *Err) ID(msg string, args ...any) Error {
|
|
mu.id = fmt.Sprintf(msg, args...)
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) GetID() string {
|
|
return mu.id
|
|
}
|
|
|
|
func (mu *Err) Log(ctx context.Context) Error {
|
|
mu.logger.LogError(ctx, mu)
|
|
return mu
|
|
}
|
|
|
|
func (mu *Err) Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error {
|
|
mu.notifier.NotifyError(ctx, mu, opts...)
|
|
return mu
|
|
}
|
|
|
|
// Sequence returns the state of the error sequence.
|
|
func (mu *Err) Sequence() *Sequence {
|
|
return mu.sequence
|
|
}
|
|
|
|
func (mu *Err) Unwrap() []error {
|
|
return mu.errs
|
|
}
|