diff --git a/builder.go b/builder.go new file mode 100644 index 0000000..8593ee1 --- /dev/null +++ b/builder.go @@ -0,0 +1,40 @@ +package goerr + +import ( + "fmt" +) + +// Builder keeps a set of key-value pairs and can create a new error and wrap error with the key-value pairs. +type Builder struct { + values values +} + +// NewBuilder creates a new Builder +func NewBuilder() *Builder { + return &Builder{values: make(values)} +} + +// With copies the current Builder and adds a new key-value pair. +func (x *Builder) With(key string, value any) *Builder { + newVS := &Builder{values: x.values.clone()} + newVS.values[key] = value + return newVS +} + +// New creates a new error with message +func (x *Builder) New(format string, args ...any) *Error { + err := newError() + err.msg = fmt.Sprintf(format, args...) + err.values = x.values.clone() + + return err +} + +// Wrap creates a new Error with caused error and add message. +func (x *Builder) Wrap(cause error, msg ...any) *Error { + err := newError() + err.msg = toWrapMessage(msg) + err.cause = cause + err.values = x.values.clone() + return err +} diff --git a/builder_test.go b/builder_test.go new file mode 100644 index 0000000..c43b160 --- /dev/null +++ b/builder_test.go @@ -0,0 +1,32 @@ +package goerr_test + +import ( + "testing" + + "github.com/m-mizutani/goerr" +) + +func newErrorWithBuilder() *goerr.Error { + return goerr.NewBuilder().With("color", "orange").New("error") +} + +func TestBuilderNew(t *testing.T) { + err := newErrorWithBuilder() + + if err.Values()["color"] != "orange" { + t.Errorf("Unexpected value: %v", err.Values()) + } +} + +func TestBuilderWrap(t *testing.T) { + cause := goerr.New("cause") + err := goerr.NewBuilder().With("color", "blue").Wrap(cause, "error") + + if err.Values()["color"] != "blue" { + t.Errorf("Unexpected value: %v", err.Values()) + } + + if err.Unwrap().Error() != "cause" { + t.Errorf("Unexpected cause: %v", err.Unwrap().Error()) + } +} diff --git a/errors.go b/errors.go index cfe5fb6..9a56503 100644 --- a/errors.go +++ b/errors.go @@ -18,18 +18,18 @@ func New(format string, args ...any) *Error { return err } +func toWrapMessage(msgs []any) string { + var newMsgs []string + for _, m := range msgs { + newMsgs = append(newMsgs, fmt.Sprintf("%v", m)) + } + return strings.Join(newMsgs, " ") +} + // Wrap creates a new Error and add message. func Wrap(cause error, msg ...any) *Error { err := newError() - - if len(msg) > 0 { - var newMsgs []string - for _, m := range msg { - newMsgs = append(newMsgs, fmt.Sprintf("%v", m)) - } - err.msg = strings.Join(newMsgs, " ") - } - + err.msg = toWrapMessage(msg) err.cause = cause return err @@ -56,19 +56,29 @@ func Unwrap(err error) *Error { return nil } +type values map[string]any + +func (x values) clone() values { + newValues := make(values) + for key, value := range x { + newValues[key] = value + } + return newValues +} + // Error is error interface for deepalert to handle related variables type Error struct { msg string id string st *stack cause error - values map[string]any + values values } func newError() *Error { return &Error{ st: callers(), - values: make(map[string]any), + values: make(values), id: uuid.New().String(), } }