From 4de43030fab5d2b603fe48c958c5a6439d957fe5 Mon Sep 17 00:00:00 2001 From: Eliot Hedeman Date: Wed, 11 Feb 2026 10:41:49 -0500 Subject: [PATCH] error messages can now be the final argument in assertions --- bench_test.go | 2 +- check.go | 72 ++++++++++++++++++++++++++++----------------------- check_test.go | 2 +- slice.go | 14 +++++----- 4 files changed, 49 insertions(+), 41 deletions(-) diff --git a/bench_test.go b/bench_test.go index 3b35a80..3337160 100644 --- a/bench_test.go +++ b/bench_test.go @@ -8,7 +8,7 @@ import ( type binOp[T cmp.Ordered] struct { name string cmp int - op func(T, T) + op func(T, T, ...string) } func binOps[T cmp.Ordered]() []binOp[T] { diff --git a/check.go b/check.go index 3cbf45b..2de4ee9 100644 --- a/check.go +++ b/check.go @@ -4,104 +4,112 @@ import ( "cmp" "errors" "fmt" + "strings" ) -func formatCmp[T any](cmp string, a, b T) string { - return fmt.Sprintf("%v %s %v", a, cmp, b) +func panicMsg(base string, msg []string) string { + if len(msg) > 0 { + return strings.Join(msg, " ") + ": " + base + } + return base +} + +func formatCmp[T any](cmp string, a, b T, msg []string) string { + return panicMsg(fmt.Sprintf("%v %s %v", a, cmp, b), msg) } // Eq compares the two input values and panics if they are not equal -func Eq[T comparable](a, b T) { +func Eq[T comparable](a, b T, msg ...string) { if a != b { - panic(formatCmp("==", a, b)) + panic(formatCmp("==", a, b, msg)) } } // NotEq compares the two input values and panics if they are equal -func NotEq[T comparable](a, b T) { +func NotEq[T comparable](a, b T, msg ...string) { if a == b { - panic(formatCmp("!=", a, b)) + panic(formatCmp("!=", a, b, msg)) } } // GT panics if a is not greater than b -func GT[T cmp.Ordered](a, b T) { +func GT[T cmp.Ordered](a, b T, msg ...string) { if a <= b { - panic(formatCmp(">", a, b)) + panic(formatCmp(">", a, b, msg)) } } // LT panics if a is not less than b -func LT[T cmp.Ordered](a, b T) { +func LT[T cmp.Ordered](a, b T, msg ...string) { if a >= b { - panic(formatCmp("<", a, b)) + panic(formatCmp("<", a, b, msg)) } } // GTE panics if a is not greater than or equal to b -func GTE[T cmp.Ordered](a, b T) { +func GTE[T cmp.Ordered](a, b T, msg ...string) { if a < b { - panic(formatCmp(">=", a, b)) + panic(formatCmp(">=", a, b, msg)) } } // LTE panics if a is not less than or equal to b -func LTE[T cmp.Ordered](a, b T) { +func LTE[T cmp.Ordered](a, b T, msg ...string) { if a > b { - panic(formatCmp(">=", a, b)) + panic(formatCmp(">=", a, b, msg)) } } -func Between[T cmp.Ordered](a, low, high T) { - GT(a, low) - LT(a, high) +func Between[T cmp.Ordered](a, low, high T, msg ...string) { + GT(a, low, msg...) + LT(a, high, msg...) } -func BetweenInclusive[T cmp.Ordered](a, low, high T) { - GTE(a, low) - LTE(a, high) +func BetweenInclusive[T cmp.Ordered](a, low, high T, msg ...string) { + GTE(a, low, msg...) + LTE(a, high, msg...) } // Nil panics if x is not nil -func Nil(x any) { +func Nil(x any, msg ...string) { if x != nil { - panic(formatCmp("!=", x, nil)) + panic(formatCmp("!=", x, nil, msg)) } } // NotNil panics if x is nil -func NotNil(x any) { +func NotNil(x any, msg ...string) { if x == nil { - panic(formatCmp("==", x, nil)) + panic(formatCmp("==", x, nil, msg)) } } // Is panics if a is not of type T -func Is[T any](a any) { +func Is[T any](a any, msg ...string) { if _, ok := a.(T); !ok { var want T - panic(fmt.Sprintf("%v != %T", a, want)) + panic(panicMsg(fmt.Sprintf("%v != %T", a, want), msg)) } } // ErrIs panics if e does not match target using errors.Is -func ErrIs(e error, target error) { +func ErrIs(e error, target error, msg ...string) { if !errors.Is(e, target) { - panic(fmt.Sprintf("%v is not %v", e, target)) + panic(panicMsg(fmt.Sprintf("%v is not %v", e, target), msg)) } } -func Must[T any](t T, err error) T { - Nil(err) +func Must[T any](t T, err error, msg ...string) T { + Nil(err, msg...) return t } // Panics executes f and panics if f does not panic -func Panics(f func()) { +func Panics(f func(), msg ...string) { defer func() { r := recover() if r == nil { - panic(fmt.Sprintf("Expected %T to panic but it did not", f)) + panic(panicMsg(fmt.Sprintf("Expected %T to panic but it did not", f), msg)) } }() f() diff --git a/check_test.go b/check_test.go index d24ce7e..85e53f2 100644 --- a/check_test.go +++ b/check_test.go @@ -20,7 +20,7 @@ func TestPanics(t *testing.T) { func TestCmp(t *testing.T) { table := []struct { a, b int - cmp func(int, int) + cmp func(int, int, ...string) passes bool }{ {1, 0, GT[int], true}, diff --git a/slice.go b/slice.go index b1b1704..d9aafbf 100644 --- a/slice.go +++ b/slice.go @@ -6,32 +6,32 @@ import ( ) // SliceEq panics if slices a and b are not element-wise equal -func SliceEq[T comparable](a, b []T) { +func SliceEq[T comparable](a, b []T, msg ...string) { if len(a) != len(b) { - panic(fmt.Sprintf("slice lengths differ: %d != %d\n a: %v\n b: %v", len(a), len(b), a, b)) + panic(panicMsg(fmt.Sprintf("slice lengths differ: %d != %d\n a: %v\n b: %v", len(a), len(b), a, b), msg)) } for i := range a { if a[i] != b[i] { - panic(fmt.Sprintf("slices differ at index %d: %v != %v", i, a[i], b[i])) + panic(panicMsg(fmt.Sprintf("slices differ at index %d: %v != %v", i, a[i], b[i]), msg)) } } } // SliceContains panics if v is not found in s -func SliceContains[T comparable](s []T, v T) { +func SliceContains[T comparable](s []T, v T, msg ...string) { for _, x := range s { if x == v { return } } - panic(fmt.Sprintf("%v not found in %v", v, s)) + panic(panicMsg(fmt.Sprintf("%v not found in %v", v, s), msg)) } // SliceSorted panics if s is not sorted in ascending order -func SliceSorted[T cmp.Ordered](s []T) { +func SliceSorted[T cmp.Ordered](s []T, msg ...string) { for i := 1; i < len(s); i++ { if s[i] < s[i-1] { - panic(fmt.Sprintf("slice not sorted at index %d, %d: %v > %v", i-1, i, s[i-1], s[i])) + panic(panicMsg(fmt.Sprintf("slice not sorted at index %d, %d: %v > %v", i-1, i, s[i-1], s[i]), msg)) } } }