Skip to content

Instantly share code, notes, and snippets.

@IlyasYOY
Created November 22, 2024 15:38
Show Gist options
  • Save IlyasYOY/8c3334f760b42eda393ffc81504f586c to your computer and use it in GitHub Desktop.
Save IlyasYOY/8c3334f760b42eda393ffc81504f586c to your computer and use it in GitHub Desktop.
make your errors detailed
package detailederror
import "errors"
type detailedError struct {
err error
key string
val string
}
func (d *detailedError) Error() string {
return d.err.Error()
}
func WithDetails(err error, key, value string) error {
return &detailedError{
err: err,
key: key,
val: value,
}
}
func GetDetail(err error, key string) (string, bool) {
nextErr := err
var derr *detailedError
for errors.As(nextErr, &derr) {
if derr.key == key {
return derr.val, true
}
nextErr = derr.err
}
return "", false
}
func GetDetails(err error) map[string]string {
nextErr := err
var derr *detailedError
details := make(map[string]string, 0)
for errors.As(nextErr, &derr) {
details[derr.key] = derr.val
nextErr = derr.err
}
return details
}
package detailederror_test
import (
"errors"
"testing"
"github.com/IlyasYOY/detailederror"
"github.com/google/go-cmp/cmp"
)
func TestWithDetails(t *testing.T) {
err := errors.New("test")
gotErr := detailederror.WithDetails(err, "user", "ilya")
if gotErr == nil {
t.Errorf("error must not be nil: %v", gotErr)
}
}
func TestGetDetail_Nested(t *testing.T) {
err := errors.New("test")
key1 := "user1"
want1 := "ilya1"
detailedErr1 := detailederror.WithDetails(err, key1, want1)
key2 := "user2"
want2 := "ilya2"
detailedErr2 := detailederror.WithDetails(detailedErr1, key2, want2)
got2, ok2 := detailederror.GetDetail(detailedErr2, key2)
got1, ok1 := detailederror.GetDetail(detailedErr2, key1)
if !ok1 {
t.Fatalf("value not found by key: %q", key1)
}
if !ok2 {
t.Fatalf("value not found by key: %q", key2)
}
if got2 != want2 {
t.Errorf("value must be equal to %q", want2)
}
if got1 != want1 {
t.Errorf("value must be equal to %q", want1)
}
}
func TestGetDetails_Nested(t *testing.T) {
err := errors.New("test")
detailedErr1 := detailederror.WithDetails(err, "user1", "ilya1")
detailedErr2 := detailederror.WithDetails(detailedErr1, "user2", "ilya2")
got := detailederror.GetDetails(detailedErr2)
if diff := cmp.Diff(map[string]string{
"user1": "ilya1",
"user2": "ilya2",
}, got); diff != "" {
t.Errorf("(-want +got):\\n%s", diff)
}
}
func TestGetDetail(t *testing.T) {
err := errors.New("test")
key := "user"
want := "ilya"
detailedErr := detailederror.WithDetails(err, key, want)
got, ok := detailederror.GetDetail(detailedErr, key)
if !ok {
t.Fatalf("value not found by key: %q", key)
}
if got != want {
t.Errorf("value must be equal to %q", want)
}
}
func TestGetDetail_Absent(t *testing.T) {
err := errors.New("test")
want := "ilya"
detailedErr := detailederror.WithDetails(err, "name", want)
_, ok := detailederror.GetDetail(detailedErr, "user")
if ok {
t.Fatalf("value was found")
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment