zen/core/zerr/err.go

230 lines
4.6 KiB
Go
Raw Normal View History

2024-08-22 17:26:53 +07:00
package zerr
import (
"context"
"fmt"
2024-08-26 16:24:41 +07:00
"io"
2024-08-22 17:26:53 +07:00
"log/slog"
"strconv"
"strings"
"time"
"connectrpc.com/connect"
2024-08-26 16:24:41 +07:00
"github.com/pborman/indent"
2024-08-22 17:26:53 +07:00
"gitlab.bareksa.com/backend/zen/core/zoptions"
)
2024-08-23 18:41:19 +07:00
var _ Error = (*Err)(nil)
2024-08-22 17:26:53 +07:00
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
2024-08-22 17:26:53 +07:00
}
2024-08-26 16:24:41 +07:00
// Join implements Error.
func (mu *Err) Join(errs ...error) Error {
for _, err := range errs {
if err != nil {
mu.errs = append(mu.errs, err)
}
}
return mu
}
// Resolve implements Error.
func (mu *Err) Resolve() error {
if len(mu.errs) == 0 {
return nil
}
return mu
}
2024-08-22 17:26:53 +07:00
func (mu *Err) Error() string {
if len(mu.errs) == 0 {
return "[nil]"
}
2024-08-26 16:24:41 +07:00
s := &strings.Builder{}
if mu.message != "" {
s.WriteString(mu.message)
}
2024-08-22 17:26:53 +07:00
if len(mu.errs) == 1 {
2024-08-26 16:24:41 +07:00
err := mu.errs[0]
if wb, ok := err.(interface{ WriteBuilder(io.Writer) }); ok {
wb.WriteBuilder(s)
return s.String()
}
next := strings.TrimPrefix(err.Error(), mu.message)
if next == "" {
return s.String()
2024-08-22 17:26:53 +07:00
}
s.WriteString(": ")
2024-08-26 16:24:41 +07:00
s.WriteString(next)
2024-08-22 17:26:53 +07:00
return s.String()
}
2024-08-26 16:24:41 +07:00
if mu.message != "" {
s.WriteString(":\n")
}
w := indent.New(s, " ")
2024-08-22 17:26:53 +07:00
for i, e := range mu.errs {
if i > 0 {
2024-08-26 16:24:41 +07:00
_, _ = io.WriteString(w, "\n")
}
_, _ = io.WriteString(w, strconv.Itoa(i+1))
_, _ = io.WriteString(w, ". ")
if wb, ok := e.(interface{ WriteBuilder(io.Writer) }); ok {
wb.WriteBuilder(w)
} else {
_, _ = io.WriteString(w, e.Error())
2024-08-22 17:26:53 +07:00
}
}
return s.String()
}
2024-08-26 16:24:41 +07:00
// LogValue returns log fields to be consumed by logger.
func (mu *Err) LogValue() slog.Value {
attrs := make([]slog.Attr, 0, 8)
if len(mu.message) > 0 {
attrs = append(attrs, slog.String("message", mu.message))
2024-08-22 17:26:53 +07:00
}
2024-08-26 16:24:41 +07:00
if len(mu.publicMessage) > 0 {
attrs = append(attrs, slog.String("public", mu.message))
2024-08-22 17:26:53 +07:00
}
2024-08-26 16:24:41 +07:00
if mu.code != connect.CodeUnknown && mu.code != connect.CodeInternal {
attrs = append(attrs, slog.String("code", mu.code.String()))
2024-08-22 17:26:53 +07:00
}
2024-08-26 16:24:41 +07:00
if mu.sequence.IsRoot() {
}
if len(mu.errs) == 1 {
2024-08-22 17:26:53 +07:00
}
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 {
2024-08-23 10:18:27 +07:00
mu.logger.LogError(ctx, mu)
2024-08-22 17:26:53 +07:00
return mu
}
func (mu *Err) Notify(ctx context.Context, opts ...zoptions.NotifyOption) Error {
2024-08-23 10:18:27 +07:00
mu.notifier.NotifyError(ctx, mu, opts...)
2024-08-22 17:26:53 +07:00
return mu
}
// Sequence returns the state of the error sequence.
func (mu *Err) Sequence() Sequence {
2024-08-22 17:26:53 +07:00
return mu.sequence
}
func (mu *Err) Unwrap() []error {
return mu.errs
}
2024-08-23 18:41:19 +07:00
// IsMulti implements Error.
func (mu *Err) IsMulti() bool {
return len(mu.errs) > 1
}