diff options
| author | Shulhan <ms@kilabit.info> | 2024-12-14 21:11:11 +0700 |
|---|---|---|
| committer | Shulhan <ms@kilabit.info> | 2024-12-28 14:41:32 +0700 |
| commit | c4dd4760f9fac2fb93ad65310550e479458fc0d1 (patch) | |
| tree | d3024ad6bae831f0a663d3dbcc1be20e1b3ce554 | |
| parent | 7979b504cdf442e13db6bcabbdb0375a60d022f0 (diff) | |
| download | pakakeh.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.go | 15 | ||||
| -rw-r--r-- | lib/reflect/equaler_example_test.go | 52 | ||||
| -rw-r--r-- | lib/reflect/equaler_test.go | 61 | ||||
| -rw-r--r-- | lib/reflect/reflect.go | 40 |
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 ( |
