diff --git a/byteutil/byteutil.go b/byteutil/byteutil.go index 1f68bcb69..dc2d50f19 100644 --- a/byteutil/byteutil.go +++ b/byteutil/byteutil.go @@ -2,9 +2,23 @@ package byteutil import ( "bytes" + "fmt" + "math/rand" + "strconv" + "time" "unsafe" ) +// Random bytes generate +func Random(length int) ([]byte, error) { + b := make([]byte, length) + // Note that err == nil only if we read len(b) bytes. + if _, err := rand.Read(b); err != nil { + return nil, err + } + return b, nil +} + // FirstLine from command output func FirstLine(bs []byte) []byte { if i := bytes.IndexByte(bs, '\n'); i >= 0 { @@ -29,7 +43,7 @@ func SafeString(bs []byte, err error) string { return string(bs) } -// String convert bytes to string +// String unsafe convert bytes to string func String(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } @@ -38,3 +52,54 @@ func String(b []byte) string { func ToString(b []byte) string { return *(*string)(unsafe.Pointer(&b)) } + +// AppendAny append any value to byte slice +func AppendAny(dst []byte, v any) []byte { + if v == nil { + return append(dst, ""...) + } + + switch val := v.(type) { + case []byte: + dst = append(dst, val...) + case string: + dst = append(dst, val...) + case int: + dst = strconv.AppendInt(dst, int64(val), 10) + case int8: + dst = strconv.AppendInt(dst, int64(val), 10) + case int16: + dst = strconv.AppendInt(dst, int64(val), 10) + case int32: + dst = strconv.AppendInt(dst, int64(val), 10) + case int64: + dst = strconv.AppendInt(dst, val, 10) + case uint: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint8: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint16: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint32: + dst = strconv.AppendUint(dst, uint64(val), 10) + case uint64: + dst = strconv.AppendUint(dst, val, 10) + case float32: + dst = strconv.AppendFloat(dst, float64(val), 'f', -1, 32) + case float64: + dst = strconv.AppendFloat(dst, val, 'f', -1, 64) + case bool: + dst = strconv.AppendBool(dst, val) + case time.Time: + dst = val.AppendFormat(dst, time.RFC3339) + case time.Duration: + dst = strconv.AppendInt(dst, int64(val), 10) + case error: + dst = append(dst, val.Error()...) + case fmt.Stringer: + dst = append(dst, val.String()...) + default: + dst = append(dst, fmt.Sprint(v)...) + } + return dst +} diff --git a/byteutil/byteutil_test.go b/byteutil/byteutil_test.go index a0efb169c..4d6b161aa 100644 --- a/byteutil/byteutil_test.go +++ b/byteutil/byteutil_test.go @@ -6,6 +6,7 @@ import ( "github.com/gookit/goutil/byteutil" "github.com/gookit/goutil/testutil/assert" + "github.com/gookit/goutil/timex" ) func TestFirstLine(t *testing.T) { @@ -32,3 +33,11 @@ func TestMd5(t *testing.T) { assert.NotEmpty(t, byteutil.Md5("abc")) assert.NotEmpty(t, byteutil.Md5([]int{12, 34})) } + +func TestAppendAny(t *testing.T) { + assert.Eq(t, []byte("123"), byteutil.AppendAny(nil, 123)) + assert.Eq(t, []byte("123"), byteutil.AppendAny([]byte{}, 123)) + assert.Eq(t, []byte("123"), byteutil.AppendAny([]byte("1"), 23)) + assert.Eq(t, []byte("1"), byteutil.AppendAny([]byte("1"), nil)) + assert.Eq(t, "3600000000000", string(byteutil.AppendAny([]byte{}, timex.OneHour))) +} diff --git a/fmtutil/format_test.go b/fmtutil/format_test.go index e78697946..18612a644 100644 --- a/fmtutil/format_test.go +++ b/fmtutil/format_test.go @@ -4,8 +4,10 @@ import ( "fmt" "testing" + "github.com/gookit/goutil/errorx" "github.com/gookit/goutil/fmtutil" "github.com/gookit/goutil/testutil/assert" + "github.com/gookit/goutil/timex" ) func TestDataSize(t *testing.T) { @@ -65,6 +67,30 @@ func TestArgsWithSpaces(t *testing.T) { assert.Eq(t, "", fmtutil.ArgsWithSpaces([]any{})) assert.Eq(t, "abc", fmtutil.ArgsWithSpaces([]any{"abc"})) assert.Eq(t, "23 abc", fmtutil.ArgsWithSpaces([]any{23, "abc"})) + + tests := []struct { + args []any + want string + }{ + {nil, ""}, + {[]any{"a", "b", "c"}, "a b c"}, + {[]any{"a", "b", "c", 1, 2, 3}, "a b c 1 2 3"}, + {[]any{"a", 1, nil}, "a 1 "}, + {[]any{12, int8(12), int16(12), int32(12), int64(12)}, "12 12 12 12 12"}, + {[]any{uint(12), uint8(12), uint16(12), uint32(12), uint64(12)}, "12 12 12 12 12"}, + {[]any{float32(12.12), 12.12}, "12.12 12.12"}, + {[]any{true, false}, "true false"}, + {[]any{[]byte("abc"), []byte("123")}, "abc 123"}, + {[]any{timex.OneHour}, "3600000000000"}, + {[]any{errorx.Raw("a error message")}, "a error message"}, + {[]any{[]int{1, 2, 3}}, "[1 2 3]"}, + } + + for _, tt := range tests { + assert.Eq(t, tt.want, fmtutil.ArgsWithSpaces(tt.args)) + } + + assert.NotEmpty(t, fmtutil.ArgsWithSpaces([]any{timex.Now().T()})) } func TestStringsToInts(t *testing.T) {