Skip to content

Commit

Permalink
Merge pull request #5 from Cleverse/feature/pure-package
Browse files Browse the repository at this point in the history
Purify Golang Implementation
  • Loading branch information
Planxnx authored Oct 23, 2023
2 parents 5e150be + 74f94d0 commit d37a2ce
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 80 deletions.
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
}
}

0 comments on commit d37a2ce

Please sign in to comment.