Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Purify Golang Implementation #5

Merged
merged 3 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down
2 changes: 1 addition & 1 deletion errors/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# utils

Optimized common generic utilities for Cleverse projects.
Pure Golang optimized generic utilities for Cleverse projects.

## Installation

Expand Down
25 changes: 4 additions & 21 deletions utils/errors.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package utils

import (
"fmt"
"reflect"

"github.com/Cleverse/go-utilities/errors"
)

// Must is used to simplify error handling.
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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
}
Expand All @@ -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:]...)
}
}
11 changes: 0 additions & 11 deletions utils/go.mod
Original file line number Diff line number Diff line change
@@ -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
)
12 changes: 0 additions & 12 deletions utils/go.sum
Original file line number Diff line number Diff line change
@@ -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=
5 changes: 2 additions & 3 deletions utils/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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)
}
11 changes: 6 additions & 5 deletions utils/hex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package utils

import (
"testing"

"github.com/stretchr/testify/assert"
)

func TestHex0XPrefix(t *testing.T) {
Expand Down Expand Up @@ -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)
}
})
}
}
46 changes: 22 additions & 24 deletions utils/struct_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package utils
import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestZeroFieldsEqualStructHasZero(t *testing.T) {
Expand All @@ -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{
Expand All @@ -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)
}
}
{
Expand All @@ -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)
}
}
}
Expand Down Expand Up @@ -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))
})
}
}
Expand Down Expand Up @@ -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))
})
}
}
Expand Down Expand Up @@ -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)
}
16 changes: 16 additions & 0 deletions utils/utils.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand Down Expand Up @@ -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:]...)
}
}
93 changes: 93 additions & 0 deletions utils/utils_test.go
Original file line number Diff line number Diff line change
@@ -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
}
}
Loading