all repos

x @ 8cb64b34fc842f4393f961cb18e2b9214a89c92e

go extra()

x/is/is.go(view raw)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package is

import (
	"bytes"
	"errors"
	"reflect"
	"strings"
	"testing"
)

func Equal[T any](tb testing.TB, expected, got T) {
	tb.Helper()

	if !areEqual(expected, got) {
		tb.Errorf("expected: %#v, got: %#v", expected, got)
	}
}

func Err(tb testing.TB, got error, expected any) {
	tb.Helper()

	if expected != nil && got == nil {
		tb.Error("got: <nil>, expected: error")
		return
	}

	switch e := expected.(type) {
	case nil:
		if got != nil {
			tb.Fatalf("unexpected error: %v", got)
		}

	case string:
		if !strings.Contains(got.Error(), e) {
			tb.Fatalf("expected: %q, got: %q", got.Error(), e)
		}

	case error:
		if !errors.Is(got, e) {
			tb.Fatalf("expected: %T(%v), got: %T(%v)", got, got, e, e)
		}

	case reflect.Type:
		target := reflect.New(e).Interface()
		if !errors.As(got, target) {
			tb.Fatalf("expected: %s, got: %T", e, got)
		}

	default:
		tb.Fatalf("unexpected type: %T", expected)
	}
}

type equaler[T any] interface{ Equal(T) bool }

func areEqual[T any](a, b T) bool {
	if isNil(a) && isNil(b) {
		return true
	}

	// some types provide .Equal(like time.Time, net.IP)
	if eq, ok := any(a).(equaler[T]); ok {
		return eq.Equal(b)
	}
	if aBytes, ok := any(a).([]byte); ok {
		bBytes := any(b).([]byte)
		return bytes.Equal(aBytes, bBytes)
	}

	return reflect.DeepEqual(a, b)
}

func isNil(v any) bool {
	if v == nil {
		return true
	}

	// non-nil interface can hold a nil value, check the underlying value.
	rv := reflect.ValueOf(v)
	switch rv.Kind() {
	case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:
		return rv.IsNil()
	default:
		return false
	}
}