Skip to content

Commit

Permalink
feat: add more task and time funcs
Browse files Browse the repository at this point in the history
  • Loading branch information
dablelv committed Jan 9, 2025
1 parent 8290911 commit c479a1b
Show file tree
Hide file tree
Showing 25 changed files with 1,303 additions and 72 deletions.
2 changes: 1 addition & 1 deletion conv/conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ func ToAnyE[T any](a any) (T, error) {
}
t = any(v).(T)
default:
return t, fmt.Errorf("The type %T isn't supported", t)
return t, fmt.Errorf("the type %T isn't supported", t)
}
return t, nil
}
11 changes: 11 additions & 0 deletions conv/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,14 @@ func jsonStringToObject(s string, v any) error {
data := []byte(s)
return json.Unmarshal(data, v)
}

// SliceToMap converts a slice of type T into a map, using a specified key extraction function.
// T can be any type, and K is the type of the keys used in the resulting map, which must be comparable.
func SliceToMap[T any, K comparable](data []T, getKey func(e T) K) map[K]T {
m := make(map[K]T, len(data))

for _, e := range data {
m[getKey(e)] = e
}
return m
}
21 changes: 21 additions & 0 deletions conv/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,24 @@ func SplitStrToSet(s string, sep string) map[string]struct{} {
}
return m
}

// SliceToSet converts a slice of type T into a map.
// T can be any type but must be comparable.
func SliceToSet[T comparable](s []T) map[T]struct{} {
m := make(map[T]struct{}, len(s))
for _, v := range s {
m[v] = struct{}{}
}
return m
}

// SliceToSetFunc converts a slice of type T into a map, using a specified key extraction function.
// T can be any type, and K is the type of the keys used in the resulting map, which must be comparable.
func SliceToSetFunc[T any, K comparable](data []T, getKey func(e T) K) map[K]struct{} {
m := make(map[K]struct{}, len(data))

for _, e := range data {
m[getKey(e)] = struct{}{}
}
return m
}
34 changes: 28 additions & 6 deletions conv/slice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,34 @@ func TestToSlice(t *testing.T) {
assert.IsNil(ss)
}

func TestJsonToSlice(t *testing.T) {
assert := utest.NewAssert(t, "TestJsonToSlice")

assert.Equal([]int{1, 2, 3}, JsonToSlice[[]int]([]byte("[1,2,3]")))
assert.Equal([]float64{1.1, 2.2, 3.3}, JsonToSlice[[]float64]([]byte("[1.1,2.2,3.3]")))
assert.Equal([]string{"foo", "bar", "baz"}, JsonToSlice[[]string]([]byte(`["foo","bar","baz"]`)))
func TestJsonToSliceE(t *testing.T) {
{
assert := utest.NewAssert(t, "success")

r1, err := JsonToSliceE[[]int]([]byte("[1,2,3]"))
assert.IsNil(err)
assert.Equal(r1, []int{1, 2, 3})

r2, err := JsonToSliceE[[]float64]([]byte("[1.1,2.2,3.3]"))
assert.IsNil(err)
assert.Equal(r2, []float64{1.1, 2.2, 3.3})

r3, err := JsonToSliceE[[]string]([]byte(`["foo","bar","baz"]`))
assert.IsNil(err)
assert.Equal(r3, []string{"foo", "bar", "baz"})
}
{
assert := utest.NewAssert(t, "success but zero value")
r, err := JsonToSliceE[[]int]([]byte(`[1,2,3,"bar"]`))
assert.IsNotNil(err)
assert.Equal(r, []int{1, 2, 3, 0})
}
{
assert := utest.NewAssert(t, "error:invalid json")
r, err := JsonToSliceE[[]int]([]byte(`[1,2,3,"bar"`))
assert.IsNotNil(err)
assert.IsNil(r)
}
}

func TestMapKeys(t *testing.T) {
Expand Down
68 changes: 52 additions & 16 deletions internal/utest/assert.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,48 +31,58 @@ func NewAssert(t *testing.T, caseName string) *Assert {
}

// Equal check if expected is equal with actual.
func (a *Assert) Equal(expected, actual any) {
if compare(expected, actual) != compareEqual {
makeTestFailed(a.T, a.CaseName, expected, actual)
func (a *Assert) Equal(actual, expected any) {
if compare(actual, expected) != compareEqual {
makeTestFailed(a.T, a.CaseName, actual, expected)
}
}

// False check if expected is false.
func (a *Assert) False(actual any) {
a.Equal(actual, false)
}

// True check if expected is true.
func (a *Assert) True(actual any) {
a.Equal(actual, true)
}

// NotEqual check if expected is not equal with actual
func (a *Assert) NotEqual(expected, actual any) {
if compare(expected, actual) == compareEqual {
func (a *Assert) NotEqual(actual, expected any) {
if compare(actual, expected) == compareEqual {
expectedInfo := fmt.Sprintf("not %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}

// Greater check if expected is greate than actual
func (a *Assert) Greater(expected, actual any) {
if compare(expected, actual) != compareGreater {
func (a *Assert) Greater(actual, expected any) {
if compare(actual, expected) != compareGreater {
expectedInfo := fmt.Sprintf("> %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}

// GreaterOrEqual check if expected is greate than or equal with actual
func (a *Assert) GreaterOrEqual(expected, actual any) {
isGreatOrEqual := compare(expected, actual) == compareGreater || compare(expected, actual) == compareEqual
func (a *Assert) GreaterOrEqual(actual, expected any) {
isGreatOrEqual := compare(actual, expected) == compareGreater || compare(actual, expected) == compareEqual
if !isGreatOrEqual {
expectedInfo := fmt.Sprintf(">= %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}

// Less check if expected is less than actual
func (a *Assert) Less(expected, actual any) {
if compare(expected, actual) != compareLess {
func (a *Assert) Less(actual, expected any) {
if compare(actual, expected) != compareLess {
expectedInfo := fmt.Sprintf("< %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
}
}

// LessOrEqual check if expected is less than or equal with actual
func (a *Assert) LessOrEqual(expected, actual any) {
isLessOrEqual := compare(expected, actual) == compareLess || compare(expected, actual) == compareEqual
func (a *Assert) LessOrEqual(actual, expected any) {
isLessOrEqual := compare(actual, expected) == compareLess || compare(actual, expected) == compareEqual
if !isLessOrEqual {
expectedInfo := fmt.Sprintf("<= %v", expected)
makeTestFailed(a.T, a.CaseName, expectedInfo, actual)
Expand All @@ -98,11 +108,34 @@ func (a *Assert) IsNotNil(v any) {
}

func (a *Assert) ErrorContains(err error, contains string) {
if err == nil {
makeTestFailed(a.T, a.CaseName, err, contains)
}
if !strings.Contains(err.Error(), contains) {
makeTestFailed(a.T, a.CaseName, contains, err.Error())
makeTestFailed(a.T, a.CaseName, err, contains)
}
}

// ShouldBeError asserts that the first argument implements the error interface.
// It also compares the first argument against the second argument if provided
// (which must be an error message string or another error value).
// func (a *Assert) IsError(actual, expected any) string {
// if !isError(actual) {
// return fmt.Sprintf(shouldBeError, reflect.TypeOf(actual))

// makeTestFailed(a.T, a.CaseName, "not nil", v)
// }

// if len(expected) == 0 {
// return success
// }

// if expected := expected[0]; !isString(expected) && !isError(expected) {
// return fmt.Sprintf(shouldBeErrorInvalidComparisonValue, reflect.TypeOf(expected))
// }
// return ShouldEqual(fmt.Sprint(actual), fmt.Sprint(expected[0]))
// }

// compare x and y return :
// x > y -> 1, x < y -> -1, x == y -> 0, x != y -> -2
func compare(x, y any) int {
Expand Down Expand Up @@ -172,9 +205,12 @@ func compare(x, y any) int {
}

// logFailedInfo make test failed and log error info.
func makeTestFailed(t *testing.T, caseName string, expected, actual any) {
func makeTestFailed(t *testing.T, caseName string, actual, expected any) {
_, file, line, _ := runtime.Caller(2)
errInfo := fmt.Sprintf("Case %v failed. file: %v, line: %v, expected: %v, actual: %v.", caseName, file, line, expected, actual)
errInfo := fmt.Sprintf("Case %v failed. file: %v, line: %v, actual: %v, expected: %v.", caseName, file, line, actual, expected)
t.Error(errInfo)
t.FailNow()
}

func isString(value interface{}) bool { _, ok := value.(string); return ok }
func isError(value interface{}) bool { _, ok := value.(error); return ok }
1 change: 0 additions & 1 deletion internal/utest/assert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ func TestAssert(t *testing.T) {

assert.IsNil(nil)
assert.IsNotNil("abc")

}
5 changes: 5 additions & 0 deletions internal/utest/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package utest

const (
shouldBeError = "Expected an error value but was '%v' instead!"
)
91 changes: 91 additions & 0 deletions slice/make.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,94 @@ func Make[E any](e E, size ...int) []E {
}
return slice
}

// Chunk divide the slice into chunks.
func Chunk[T any](slice []T, chunkSize int) [][]T {
var chunks [][]T
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}
chunks = append(chunks, slice[i:end])
}
return chunks
}

// Filter filter out elements that do not meet the conditions.
func Filter[T any](data []T, retain func(T) bool) []T {
res := make([]T, 0, len(data))

for _, e := range data {
if retain(e) {
res = append(res, e)
}
}
return res
}

// Map applies a transformation function to each element of the input slice
// and returns a new slice containing the results.
// T is the type of the input slice elements, and U is the type of the output slice elements.
func Map[T, U any](data []T, f func(T) U) []U {
res := make([]U, 0, len(data))

for _, e := range data {
res = append(res, f(e))
}
return res
}

// GroupFunc groups elements of the input slice based on a key extraction function.
// T is the type of the input slice elements, and U is the type of the keys.
// The keys must be comparable, meaning they can be used as map keys.
func GroupFunc[T, U comparable](data []T, key func(T) U) map[U][]T {
res := make(map[U][]T, len(data))

for _, e := range data {
res[key(e)] = append(res[key(e)], e)
}
return res
}

// Distinct returns a new slice containing only the unique elements from the input slice.
// T is the type of the input slice elements, and it must be comparable.
func Distinct[T comparable](data []T) []T {
r := make([]T, 0, len(data))
m := make(map[T]struct{}, len(data))

for _, n := range data {
if _, ok := m[n]; !ok {
r = append(r, n)
}
m[n] = struct{}{}
}
return r
}

// DistinctFunc returns a new slice containing only the unique elements from the input slice,
// based on a key extraction function that determines the uniqueness of each element.
// T is the type of the input slice elements, and TK is the type of the keys used for comparison.
// TK must be comparable, meaning it can be used as a key in a map.
func DistinctFunc[T any, K comparable](data []T, key func(item T) K) []T {
r := make([]T, 0, len(data))
m := make(map[K]struct{}, len(data))
for _, v := range data {
k := key(v)
if _, ok := m[k]; !ok {
r = append(r, v)
}
m[k] = struct{}{}
}
return r
}

// Merge takes multiple slices of type T and combines them into a single slice.
// T can be any type, allowing this function to work with slices of different data types.
func Merge[T any](ss ...[]T) []T {
var r []T
for _, s := range ss {
r = append(r, s...)
}
return r
}
Loading

0 comments on commit c479a1b

Please sign in to comment.