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.

Revisions

  1. IlyasYOY created this gist Nov 22, 2024.
    44 changes: 44 additions & 0 deletions detailederror.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    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
    }
    88 changes: 88 additions & 0 deletions detailederror_test.go
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,88 @@
    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")
    }
    }