diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ec57ad8..ced92282 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ Adding: - lo.ErrorAs +- lo.TryOr +- lo.TryOrX ## 1.28.0 (2022-09-05) diff --git a/README.md b/README.md index cf06cee9..8fc3272f 100644 --- a/README.md +++ b/README.md @@ -209,6 +209,9 @@ Error handling: - [Must](#must) - [Try](#try) +- [Try1 -> Try6](#try0-6) +- [TryOr](#tryor) +- [TryOr1 -> TryOr6](#tryor0-6) - [TryCatch](#trycatch) - [TryWithErrorValue](#trywitherrorvalue) - [TryCatchWithErrorValue](#trycatchwitherrorvalue) @@ -2026,6 +2029,45 @@ ok := lo.Try2(func() (string, error) { // false ``` +### TryOr + +Calls the function and return a default value in case of error and on panic. + +```go +str, ok := lo.TryOr(func() (string, error) { + panic("error") + return "hello", nil +}, "world") +// world +// false + +ok := lo.TryOr(func() error { + return "hello", nil +}, "world") +// hello +// true + +ok := lo.TryOr(func() error { + return "hello", fmt.Errorf("error") +}, "world") +// world +// false +``` + +### TryOr{0->6} + +The same behavior than `TryOr`, but callback returns 2 variables. + +```go +str, nbr, ok := lo.TryOr2(func() (string, int, error) { + panic("error") + return "hello", 42, nil +}, "world", 21) +// world +// 21 +// false +``` + ### TryWithErrorValue The same behavior than `Try`, but also returns value passed to panic. diff --git a/errors.go b/errors.go index 6427db18..f61c2917 100644 --- a/errors.go +++ b/errors.go @@ -167,6 +167,116 @@ func Try6[T, R, S, Q, U any](callback func() (T, R, S, Q, U, error)) bool { }) } +// TryOr has the same behavior than Must, but returns a default value in case of error. +func TryOr[A any](callback func() (A, error), fallbackA A) (A, bool) { + return TryOr1(callback, fallbackA) +} + +// TryOr1 has the same behavior than Must, but returns a default value in case of error. +func TryOr1[A any](callback func() (A, error), fallbackA A) (A, bool) { + ok := false + + Try0(func() { + a, err := callback() + if err == nil { + fallbackA = a + ok = true + } + }) + + return fallbackA, ok +} + +// TryOr2 has the same behavior than Must, but returns a default value in case of error. +func TryOr2[A any, B any](callback func() (A, B, error), fallbackA A, fallbackB B) (A, B, bool) { + ok := false + + Try0(func() { + a, b, err := callback() + if err == nil { + fallbackA = a + fallbackB = b + ok = true + } + }) + + return fallbackA, fallbackB, ok +} + +// TryOr3 has the same behavior than Must, but returns a default value in case of error. +func TryOr3[A any, B any, C any](callback func() (A, B, C, error), fallbackA A, fallbackB B, fallbackC C) (A, B, C, bool) { + ok := false + + Try0(func() { + a, b, c, err := callback() + if err == nil { + fallbackA = a + fallbackB = b + fallbackC = c + ok = true + } + }) + + return fallbackA, fallbackB, fallbackC, ok +} + +// TryOr4 has the same behavior than Must, but returns a default value in case of error. +func TryOr4[A any, B any, C any, D any](callback func() (A, B, C, D, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D) (A, B, C, D, bool) { + ok := false + + Try0(func() { + a, b, c, d, err := callback() + if err == nil { + fallbackA = a + fallbackB = b + fallbackC = c + fallbackD = d + ok = true + } + }) + + return fallbackA, fallbackB, fallbackC, fallbackD, ok +} + +// TryOr5 has the same behavior than Must, but returns a default value in case of error. +func TryOr5[A any, B any, C any, D any, E any](callback func() (A, B, C, D, E, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E) (A, B, C, D, E, bool) { + ok := false + + Try0(func() { + a, b, c, d, e, err := callback() + if err == nil { + fallbackA = a + fallbackB = b + fallbackC = c + fallbackD = d + fallbackE = e + ok = true + } + }) + + return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, ok +} + +// TryOr6 has the same behavior than Must, but returns a default value in case of error. +func TryOr6[A any, B any, C any, D any, E any, F any](callback func() (A, B, C, D, E, F, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E, fallbackF F) (A, B, C, D, E, F, bool) { + ok := false + + Try0(func() { + a, b, c, d, e, f, err := callback() + if err == nil { + fallbackA = a + fallbackB = b + fallbackC = c + fallbackD = d + fallbackE = e + fallbackF = f + ok = true + } + }) + + return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, fallbackF, ok +} + // TryWithErrorValue has the same behavior than Try, but also returns value passed to panic. func TryWithErrorValue(callback func() error) (errorValue any, ok bool) { ok = true diff --git a/errors_test.go b/errors_test.go index 976efe74..34ef0b27 100644 --- a/errors_test.go +++ b/errors_test.go @@ -9,6 +9,7 @@ import ( ) func TestMust(t *testing.T) { + t.Parallel() is := assert.New(t) is.Equal("foo", Must("foo", nil)) @@ -35,6 +36,7 @@ func TestMust(t *testing.T) { } func TestMustX(t *testing.T) { + t.Parallel() is := assert.New(t) { @@ -225,6 +227,7 @@ func TestMustX(t *testing.T) { } func TestTry(t *testing.T) { + t.Parallel() is := assert.New(t) is.False(Try(func() error { @@ -239,6 +242,7 @@ func TestTry(t *testing.T) { } func TestTryX(t *testing.T) { + t.Parallel() is := assert.New(t) is.True(Try2(func() (string, error) { @@ -302,7 +306,166 @@ func TestTryX(t *testing.T) { })) } +func TestTryOr(t *testing.T) { + t.Parallel() + is := assert.New(t) + + a1, ok1 := TryOr(func() (int, error) { panic("error") }, 42) + a2, ok2 := TryOr(func() (int, error) { return 21, assert.AnError }, 42) + a3, ok3 := TryOr(func() (int, error) { return 21, nil }, 42) + + is.Equal(42, a1) + is.False(ok1) + + is.Equal(42, a2) + is.False(ok2) + + is.Equal(21, a3) + is.True(ok3) +} + +func TestTryOrX(t *testing.T) { + t.Parallel() + is := assert.New(t) + + { + a1, ok1 := TryOr1(func() (int, error) { panic("error") }, 42) + a2, ok2 := TryOr1(func() (int, error) { return 21, assert.AnError }, 42) + a3, ok3 := TryOr1(func() (int, error) { return 21, nil }, 42) + + is.Equal(42, a1) + is.False(ok1) + + is.Equal(42, a2) + is.False(ok2) + + is.Equal(21, a3) + is.True(ok3) + } + + { + a1, b1, ok1 := TryOr2(func() (int, string, error) { panic("error") }, 42, "hello") + a2, b2, ok2 := TryOr2(func() (int, string, error) { return 21, "world", assert.AnError }, 42, "hello") + a3, b3, ok3 := TryOr2(func() (int, string, error) { return 21, "world", nil }, 42, "hello") + + is.Equal(42, a1) + is.Equal("hello", b1) + is.False(ok1) + + is.Equal(42, a2) + is.Equal("hello", b2) + is.False(ok2) + + is.Equal(21, a3) + is.Equal("world", b3) + is.True(ok3) + } + + { + a1, b1, c1, ok1 := TryOr3(func() (int, string, bool, error) { panic("error") }, 42, "hello", false) + a2, b2, c2, ok2 := TryOr3(func() (int, string, bool, error) { return 21, "world", true, assert.AnError }, 42, "hello", false) + a3, b3, c3, ok3 := TryOr3(func() (int, string, bool, error) { return 21, "world", true, nil }, 42, "hello", false) + + is.Equal(42, a1) + is.Equal("hello", b1) + is.Equal(false, c1) + is.False(ok1) + + is.Equal(42, a2) + is.Equal("hello", b2) + is.Equal(false, c2) + is.False(ok2) + + is.Equal(21, a3) + is.Equal("world", b3) + is.Equal(true, c3) + is.True(ok3) + } + + { + a1, b1, c1, d1, ok1 := TryOr4(func() (int, string, bool, int, error) { panic("error") }, 42, "hello", false, 42) + a2, b2, c2, d2, ok2 := TryOr4(func() (int, string, bool, int, error) { return 21, "world", true, 21, assert.AnError }, 42, "hello", false, 42) + a3, b3, c3, d3, ok3 := TryOr4(func() (int, string, bool, int, error) { return 21, "world", true, 21, nil }, 42, "hello", false, 42) + + is.Equal(42, a1) + is.Equal("hello", b1) + is.Equal(false, c1) + is.Equal(42, d1) + is.False(ok1) + + is.Equal(42, a2) + is.Equal("hello", b2) + is.Equal(false, c2) + is.Equal(42, d2) + is.False(ok2) + + is.Equal(21, a3) + is.Equal("world", b3) + is.Equal(true, c3) + is.Equal(21, d3) + is.True(ok3) + } + + { + a1, b1, c1, d1, e1, ok1 := TryOr5(func() (int, string, bool, int, int, error) { panic("error") }, 42, "hello", false, 42, 42) + a2, b2, c2, d2, e2, ok2 := TryOr5(func() (int, string, bool, int, int, error) { return 21, "world", true, 21, 21, assert.AnError }, 42, "hello", false, 42, 42) + a3, b3, c3, d3, e3, ok3 := TryOr5(func() (int, string, bool, int, int, error) { return 21, "world", true, 21, 21, nil }, 42, "hello", false, 42, 42) + + is.Equal(42, a1) + is.Equal("hello", b1) + is.Equal(false, c1) + is.Equal(42, d1) + is.Equal(42, e1) + is.False(ok1) + + is.Equal(42, a2) + is.Equal("hello", b2) + is.Equal(false, c2) + is.Equal(42, d2) + is.Equal(42, e2) + is.False(ok2) + + is.Equal(21, a3) + is.Equal("world", b3) + is.Equal(true, c3) + is.Equal(21, d3) + is.Equal(21, e3) + is.True(ok3) + } + + { + a1, b1, c1, d1, e1, f1, ok1 := TryOr6(func() (int, string, bool, int, int, int, error) { panic("error") }, 42, "hello", false, 42, 42, 42) + a2, b2, c2, d2, e2, f2, ok2 := TryOr6(func() (int, string, bool, int, int, int, error) { return 21, "world", true, 21, 21, 21, assert.AnError }, 42, "hello", false, 42, 42, 42) + a3, b3, c3, d3, e3, f3, ok3 := TryOr6(func() (int, string, bool, int, int, int, error) { return 21, "world", true, 21, 21, 21, nil }, 42, "hello", false, 42, 42, 42) + + is.Equal(42, a1) + is.Equal("hello", b1) + is.Equal(false, c1) + is.Equal(42, d1) + is.Equal(42, e1) + is.Equal(42, f1) + is.False(ok1) + + is.Equal(42, a2) + is.Equal("hello", b2) + is.Equal(false, c2) + is.Equal(42, d2) + is.Equal(42, e2) + is.Equal(42, f2) + is.False(ok2) + + is.Equal(21, a3) + is.Equal("world", b3) + is.Equal(true, c3) + is.Equal(21, d3) + is.Equal(21, e3) + is.Equal(21, f3) + is.True(ok3) + } +} + func TestTryWithErrorValue(t *testing.T) { + t.Parallel() is := assert.New(t) err, ok := TryWithErrorValue(func() error { @@ -319,6 +482,7 @@ func TestTryWithErrorValue(t *testing.T) { } func TestTryCatch(t *testing.T) { + t.Parallel() is := assert.New(t) caught := false @@ -341,6 +505,7 @@ func TestTryCatch(t *testing.T) { } func TestTryCatchWithErrorValue(t *testing.T) { + t.Parallel() is := assert.New(t) caught := false @@ -367,10 +532,11 @@ type internalError struct { } func (e *internalError) Error() string { - return fmt.Sprintf("internal error") + return "internal error" } func TestErrorsAs(t *testing.T) { + t.Parallel() is := assert.New(t) err, ok := ErrorsAs[*internalError](fmt.Errorf("hello world"))