diff --git a/README.md b/README.md index c428bca..b24dd5f 100644 --- a/README.md +++ b/README.md @@ -7,13 +7,13 @@ Miscellaneous useful Go packages by [Cleverse](https://about.cleverse.com) ## utils -Optimized common generic utilities for Cleverse Golang projects. +Pure Golang optimized generic utilities for Cleverse projects. [See here](utils/README.md). ## errors -Package errors adds stacktrace support to errors in go. +Pure Golang errors library with stacktrace support (for wrapping and formatting an errors). [See here](errors/README.md). diff --git a/errors/README.md b/errors/README.md index 242e942..eca7dfa 100644 --- a/errors/README.md +++ b/errors/README.md @@ -3,7 +3,7 @@ # errors -Package errors adds stacktrace support to errors in go. +Pure Golang errors library with stacktrace support (for wrapping and formatting an errors). ## Installation diff --git a/utils/README.md b/utils/README.md index acc27bd..e694b87 100644 --- a/utils/README.md +++ b/utils/README.md @@ -3,7 +3,7 @@ # utils -Optimized common generic utilities for Cleverse projects. +Pure Golang optimized generic utilities for Cleverse projects. ## Installation diff --git a/utils/errors.go b/utils/errors.go index 6d5507b..6d1f664 100644 --- a/utils/errors.go +++ b/utils/errors.go @@ -1,10 +1,7 @@ package utils import ( - "fmt" "reflect" - - "github.com/Cleverse/go-utilities/errors" ) // Must is used to simplify error handling. @@ -21,7 +18,7 @@ func Must[T any](data T, err any, messageArgs ...interface{}) T { // warning: this is not safe, use with caution!! (avoid to use it's in runtime) func MustNotError[T any](data T, err error) T { if err != nil { - panic(errors.WithStack(err)) + panic(err) } return data } @@ -40,7 +37,7 @@ func UnsafeMust[T any, E any](data T, e E) T { // warning: this is not safe, use with caution!! (avoid to use it's in runtime) func MustOK[T any](data T, ok bool) T { if !ok { - panic(errors.Errorf("got not ok, but should ok")) + panic("got not ok, but should ok") } return data } @@ -51,7 +48,7 @@ func MustOK[T any](data T, ok bool) T { // warning: this is not safe, use with caution!! (avoid to use it's in runtime) func MustNotOK[T any](data T, ok bool) T { if ok { - panic(errors.Errorf("got ok, but should not ok")) + panic("got ok, but should not ok") } return data } @@ -75,22 +72,8 @@ func must(err any, messageArgs ...interface{}) { if message != "" { panic(message + ": " + e.Error()) } - panic(errors.WithStack(e)) + panic(e) default: panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error") } } - -func msgFormatter(msgAndArgs ...interface{}) string { - switch len(msgAndArgs) { - case 0: - return "" - case 1: - if msgAsStr, ok := msgAndArgs[0].(string); ok { - return msgAsStr - } - return fmt.Sprintf("%+v", msgAndArgs[0]) - default: - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - } -} diff --git a/utils/go.mod b/utils/go.mod index 80b2c3a..e7d653d 100644 --- a/utils/go.mod +++ b/utils/go.mod @@ -1,14 +1,3 @@ module github.com/Cleverse/go-utilities/utils go 1.19 - -require ( - github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09 - github.com/stretchr/testify v1.8.4 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/utils/go.sum b/utils/go.sum index 814bfe2..e69de29 100644 --- a/utils/go.sum +++ b/utils/go.sum @@ -1,12 +0,0 @@ -github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09 h1:kNAgub14cUBr7uoTxQSrf0qiMNJSzMhIDa8RFdv6hkk= -github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09/go.mod h1:1QK+h746G1DwellQ6KK2rBCJusZqIDTZ9QFVGnUX9+Q= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/utils/hex.go b/utils/hex.go index d4bb114..51db106 100644 --- a/utils/hex.go +++ b/utils/hex.go @@ -2,8 +2,7 @@ package utils import ( "encoding/hex" - - "github.com/Cleverse/go-utilities/errors" + "fmt" ) // RandomHex returns a random hex string with the given length. @@ -62,5 +61,5 @@ func isHexCharacter(c byte) bool { // DecodeHex decodes a hex string into a byte slice. str can be prefixed with 0x. func DecodeHex(str string) ([]byte, error) { b, err := hex.DecodeString(Trim0xPrefix(str)) - return b, errors.WithStack(err) + return b, fmt.Errorf("decode hex: %w", err) } diff --git a/utils/hex_test.go b/utils/hex_test.go index 2ebd0e5..0b0b04b 100644 --- a/utils/hex_test.go +++ b/utils/hex_test.go @@ -2,8 +2,6 @@ package utils import ( "testing" - - "github.com/stretchr/testify/assert" ) func TestHex0XPrefix(t *testing.T) { @@ -74,9 +72,12 @@ func TestHex0XPrefix(t *testing.T) { for _, tc := range testCases { t.Run(tc.Input, func(t *testing.T) { - assert := assert.New(t) - assert.Equal(tc.Has0xPrefix, Has0xPrefix(tc.Input), "Has0xPrefix should be equal") - assert.Equal(tc.Expected, tc.Func(tc.Input), "actual result from `Func(string) string` should equal to expected") + if isHasPrefix := Has0xPrefix(tc.Input); tc.Has0xPrefix != isHasPrefix { + t.Errorf("expected Has0xPrefix to be %v, but got %v", tc.Has0xPrefix, isHasPrefix) + } + if actual := tc.Func(tc.Input); tc.Expected != actual { + t.Errorf("expected result from `Func(string) string` to be %v, but got %v", tc.Expected, actual) + } }) } } diff --git a/utils/struct_test.go b/utils/struct_test.go index d34857c..51dd2d9 100644 --- a/utils/struct_test.go +++ b/utils/struct_test.go @@ -3,8 +3,6 @@ package utils import ( "fmt" "testing" - - "github.com/stretchr/testify/assert" ) func TestZeroFieldsEqualStructHasZero(t *testing.T) { @@ -29,8 +27,8 @@ func TestZeroFieldsEqualStructHasZero(t *testing.T) { } isZero_01 := len(StructZeroFields(val)) > 0 isZero_02 := StructHasZero(val) - assert.Equal(t, isZero_01, isZero_02) - assert.True(t, isZero_01) + assertEqual(t, isZero_01, isZero_02) + assertTrue(t, isZero_01) } { val := ABC{ @@ -43,14 +41,14 @@ func TestZeroFieldsEqualStructHasZero(t *testing.T) { { isZero_01 := len(StructZeroFields(val)) > 0 isZero_02 := StructHasZero(val) - assert.Equal(t, isZero_01, isZero_02) - assert.False(t, isZero_01) + assertEqual(t, isZero_01, isZero_02) + assertFalse(t, isZero_01) } { isZero_01 := len(StructZeroFields(val, true)) > 0 isZero_02 := StructHasZero(val, true) - assert.Equal(t, isZero_01, isZero_02) - assert.True(t, isZero_01) + assertEqual(t, isZero_01, isZero_02) + assertTrue(t, isZero_01) } } { @@ -65,14 +63,14 @@ func TestZeroFieldsEqualStructHasZero(t *testing.T) { { isZero_01 := len(StructZeroFields(val)) > 0 isZero_02 := StructHasZero(val) - assert.Equal(t, isZero_01, isZero_02) - assert.False(t, isZero_01) + assertEqual(t, isZero_01, isZero_02) + assertFalse(t, isZero_01) } { isZero_01 := len(StructZeroFields(val, true)) > 0 isZero_02 := StructHasZero(val, true) - assert.Equal(t, isZero_01, isZero_02) - assert.True(t, isZero_01) + assertEqual(t, isZero_01, isZero_02) + assertTrue(t, isZero_01) } } } @@ -148,7 +146,7 @@ func TestStructZeroFields(t *testing.T) { for i, testSpec := range testSpecs { t.Run(fmt.Sprint("#", i+1), func(t *testing.T) { - assert.Equal(t, testSpec.ExpectedFields, StructZeroFields(testSpec.Struct, testSpec.CheckNested)) + assertEqualAny(t, testSpec.ExpectedFields, StructZeroFields(testSpec.Struct, testSpec.CheckNested)) }) } } @@ -229,7 +227,7 @@ func TestStructHasZero(t *testing.T) { for i, testSpec := range testSpecs { t.Run(fmt.Sprint("#", i+1), func(t *testing.T) { - assert.Equal(t, testSpec.ExpectedZero, StructHasZero(testSpec.Struct)) + assertEqual(t, testSpec.ExpectedZero, StructHasZero(testSpec.Struct)) }) } } @@ -270,14 +268,14 @@ func TestMerge(t *testing.T) { result := Merge(&to, from) - assert.Equal(t, from.A, result.A) - assert.NotEqual(t, org.A, result.A) - assert.Equal(t, org.B, result.B) - assert.NotEqual(t, from.B, result.B) - assert.Equal(t, from.C, result.C) - assert.NotEqual(t, org.C, result.C) - assert.Equal(t, org.D, result.D) - assert.NotEqual(t, from.D, result.D) - assert.Equal(t, from.E, result.E) - assert.NotEqual(t, org.E, result.E) + assertEqual(t, from.A, result.A) + assertNotEqual(t, org.A, result.A) + assertEqual(t, org.B, result.B) + assertNotEqual(t, from.B, result.B) + assertEqual(t, from.C, result.C) + assertNotEqual(t, org.C, result.C) + assertEqual(t, org.D, result.D) + assertNotEqual(t, from.D, result.D) + assertEqual(t, from.E, result.E) + assertNotEqual(t, org.E, result.E) } diff --git a/utils/utils.go b/utils/utils.go index 585f57e..fcac267 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -1,5 +1,7 @@ package utils +import "fmt" + // Default inspired by Nullish coalescing operator (??) in JavaScript // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing func Default[T comparable](value T, defaultValue T) (result T) { @@ -40,3 +42,17 @@ func Empty[T any]() T { var zero T return zero } + +func msgFormatter(msgAndArgs ...interface{}) string { + switch len(msgAndArgs) { + case 0: + return "" + case 1: + if msgAsStr, ok := msgAndArgs[0].(string); ok { + return msgAsStr + } + return fmt.Sprintf("%+v", msgAndArgs[0]) + default: + return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } +} diff --git a/utils/utils_test.go b/utils/utils_test.go new file mode 100644 index 0000000..90e20fe --- /dev/null +++ b/utils/utils_test.go @@ -0,0 +1,93 @@ +package utils + +import ( + "bytes" + "reflect" + "testing" +) + +// assertEqual asserts that expected and actual are equal. +func assertEqual[T comparable](t *testing.T, expected, actual T, msgAndArgs ...interface{}) bool { + if expected != actual { + if msg := msgFormatter(msgAndArgs...); msg != "" { + t.Error(msg) + } else { + t.Errorf("expected: %v, got: %v", expected, actual) + } + return false + } + return true +} + +func assertNotEqual[T comparable](t *testing.T, expected, actual T, msgAndArgs ...interface{}) bool { + if expected == actual { + if msg := msgFormatter(msgAndArgs...); msg != "" { + t.Error(msg) + } else { + t.Errorf("expected: %v not equal to: %v", expected, actual) + } + return false + } + return true +} + +// assertNotEqual asserts that expected and actual are not equal. +func assertTrue(t *testing.T, actual bool, msgAndArgs ...interface{}) bool { + return assertEqual(t, true, actual, msgAndArgs...) +} + +// assertNotEqual asserts that expected and actual are not equal. +func assertFalse(t *testing.T, actual bool, msgAndArgs ...interface{}) bool { + return assertEqual(t, false, actual, msgAndArgs...) +} + +func assertEqualAny(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) (equal bool) { + // check nil + switch { + case expected == nil && actual == nil: + return true + case expected == nil && actual != nil: + fallthrough + case expected != nil && actual == nil: + t.Errorf("expected: %v, got: %v", expected, actual) + return false + } + + // if types are different, they are not equal. + t1 := reflect.TypeOf(expected) + t2 := reflect.TypeOf(actual) + if t1 != t2 { + t.Errorf("type mismatch: expected: %v, got: %v", t1, t2) + return false + } + + defer func() { + if !equal { + if msg := msgFormatter(msgAndArgs...); msg != "" { + t.Error(msg) + } else { + t.Errorf("expected: %v, got: %v", expected, actual) + } + } + }() + + // default compare + switch expected := expected.(type) { + case []byte: + actual := actual.([]byte) + return bytes.Equal(expected, actual) + case []string: + actual := actual.([]string) + if len(expected) != len(actual) { + return false + } + for i := range expected { + if expected[i] != actual[i] { + return false + } + } + return true + default: + return expected == actual + } +}