aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShulhan <ms@kilabit.info>2024-12-14 21:11:11 +0700
committerShulhan <ms@kilabit.info>2024-12-28 14:41:32 +0700
commitc4dd4760f9fac2fb93ad65310550e479458fc0d1 (patch)
treed3024ad6bae831f0a663d3dbcc1be20e1b3ce554
parent7979b504cdf442e13db6bcabbdb0375a60d022f0 (diff)
downloadpakakeh.go-c4dd4760f9fac2fb93ad65310550e479458fc0d1.tar.xz
lib/reflect: refactor the Equaler method Equal
This changes the Equal signature from "Equal(v any) bool" to "Equal(v any) error". The reason for this changes is to force the method to return an error message that is understand-able by caller. While at it, simplify checking for Equaler interface in internal doEqualStruct function.
-rw-r--r--lib/reflect/equaler.go15
-rw-r--r--lib/reflect/equaler_example_test.go52
-rw-r--r--lib/reflect/equaler_test.go61
-rw-r--r--lib/reflect/reflect.go40
4 files changed, 140 insertions, 28 deletions
diff --git a/lib/reflect/equaler.go b/lib/reflect/equaler.go
index f4dc7268..8a0a8dd1 100644
--- a/lib/reflect/equaler.go
+++ b/lib/reflect/equaler.go
@@ -1,11 +1,14 @@
-// Copyright 2020, Shulhan <ms@kilabit.info>. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
package reflect
-// Equaler is an interface that when implemented by a type, it will be used to
-// compare the value in Assert.
+// Equaler is an interface that when implemented by a struct type, it will
+// be used to compare the value in [DoEqual] or [IsEqual].
type Equaler interface {
- IsEqual(v interface{}) bool
+ // Equal compare the struct receiver with parameter v.
+ // The v value can be converted to struct type T using (*T).
+ // If both struct values are equal it should return nil.
+ Equal(v any) error
}
diff --git a/lib/reflect/equaler_example_test.go b/lib/reflect/equaler_example_test.go
new file mode 100644
index 00000000..6f14d2ae
--- /dev/null
+++ b/lib/reflect/equaler_example_test.go
@@ -0,0 +1,52 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+package reflect
+
+import (
+ "fmt"
+ "log"
+)
+
+type ADT struct {
+ vint int
+}
+
+func (rnp *ADT) Equal(v any) (err error) {
+ var (
+ logp = `Equal`
+ got *ADT
+ ok bool
+ )
+ got, ok = v.(*ADT)
+ if !ok {
+ return fmt.Errorf(`%s: v type is %T, want %T`, logp, got, v)
+ }
+ if rnp.vint != got.vint {
+ return fmt.Errorf(`%s: vint: %d, want %d`,
+ logp, got.vint, rnp.vint)
+ }
+ return nil
+}
+
+func ExampleEqualer() {
+ var (
+ rp1 = ADT{
+ vint: 1,
+ }
+ rp2 = ADT{
+ vint: 2,
+ }
+ )
+ var err = DoEqual(&rp1, &rp2)
+ if err == nil {
+ log.Fatal(`expecting error, got nil`)
+ }
+
+ var exp = `Equal: vint: want 1, got 2`
+ var got = err.Error()
+ if exp != got {
+ log.Fatalf(`want %q, got %q`, exp, got)
+ }
+}
diff --git a/lib/reflect/equaler_test.go b/lib/reflect/equaler_test.go
new file mode 100644
index 00000000..0477c955
--- /dev/null
+++ b/lib/reflect/equaler_test.go
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: 2024 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+package reflect
+
+import (
+ "fmt"
+ "testing"
+)
+
+type recvNotPointer struct {
+ vint int
+}
+
+func (rnp recvNotPointer) Equal(v any) (err error) {
+ var (
+ logp = `Equal`
+ got *recvNotPointer
+ ok bool
+ )
+ got, ok = v.(*recvNotPointer)
+ if !ok {
+ return fmt.Errorf(`%s: v type is %T, want %T`, logp, v, got)
+ }
+ if rnp.vint != got.vint {
+ return fmt.Errorf(`%s: vint: want %d, got %d`,
+ logp, rnp.vint, got.vint)
+ }
+ return nil
+}
+
+func TestEqualerRecvNotPointer(t *testing.T) {
+ var (
+ rnp1 = recvNotPointer{
+ vint: 1,
+ }
+ rnp2 = recvNotPointer{
+ vint: 2,
+ }
+ )
+
+ var err = DoEqual(&rnp1, &rnp2)
+ if err == nil {
+ t.Fatal(`expecting error, got nil`)
+ }
+
+ var exp = `Equal: vint: want 1, got 2`
+ var got = err.Error()
+ if exp != got {
+ t.Fatalf(`want %q, got %q`, exp, got)
+ }
+
+ var rnp3 = recvNotPointer{
+ vint: 1,
+ }
+ err = DoEqual(&rnp1, &rnp3)
+ if err != nil {
+ t.Fatalf(`expecting no error, got %s`, err)
+ }
+}
diff --git a/lib/reflect/reflect.go b/lib/reflect/reflect.go
index 41790c55..f5f917f5 100644
--- a/lib/reflect/reflect.go
+++ b/lib/reflect/reflect.go
@@ -1,6 +1,6 @@
-// Copyright 2020, Shulhan <ms@kilabit.info>. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
+// SPDX-FileCopyrightText: 2020 M. Shulhan <ms@kilabit.info>
+//
+// SPDX-License-Identifier: BSD-3-Clause
// Package reflect extends the standard reflect package.
package reflect
@@ -17,9 +17,10 @@ const (
tagNoEqual = `noequal`
)
-// DoEqual is a naive interfaces comparison that check and use Equaler
-// interface and return an error if its not match.
+// DoEqual is a naive interfaces comparison for two values.
//
+// If the type is a struct and implement [Equaler] interface it will use the
+// [Equal] method in that struct to compare the values.
// A struct's field tagged with `noequal:""` will be skipped from being
// processed.
func DoEqual(x, y interface{}) (err error) {
@@ -38,9 +39,10 @@ func DoEqual(x, y interface{}) (err error) {
return nil
}
-// IsEqual is a naive interfaces comparison that check and use Equaler
-// interface.
+// IsEqual is a naive interfaces comparison for two values.
//
+// If the type is a struct and implement [Equaler] interface it will use the
+// [Equal] method in that struct to compare the values.
// A struct's field tagged with `noequal:""` will be skipped from being
// processed.
func IsEqual(x, y interface{}) bool {
@@ -767,22 +769,16 @@ func doEqualMap(v1, v2 reflect.Value) (err error) {
// fields has equal value.
// The type of both struct is already equal when this function called.
func doEqualStruct(v1, v2 reflect.Value) (err error) {
- var (
- m1 = v1.MethodByName(`IsEqual`)
-
- callIn []reflect.Value
- callOut []reflect.Value
- )
-
- if m1.IsValid() {
- callIn = append(callIn, v2.Addr())
- callOut = m1.Call(callIn)
- if len(callOut) == 1 && callOut[0].Kind() == reflect.Bool {
- if callOut[0].Bool() {
- return nil
- }
- return fmt.Errorf("IsEqual: %s.IsEqual(%s) return false", v1.String(), v2.String())
+ var equalerType = reflect.TypeOf((*Equaler)(nil)).Elem()
+ if v1.Type().Implements(equalerType) {
+ var m1 = v1.MethodByName(`Equal`)
+ var callIn = []reflect.Value{v2.Addr()}
+ var callOut []reflect.Value = m1.Call(callIn)
+ var val = callOut[0].Interface()
+ if val == nil {
+ return nil
}
+ return val.(error)
}
var (