mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-10-13 02:14:52 +00:00
feat[golang]: 添加一些go的常用工具类
This commit is contained in:
1 parent
0c4029ffad
commit
180906e577
24 files changed
+2420
No files matched your search
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package assert
|
||||
|
||||
import "testing"
|
||||
|
||||
// Equalf asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equalf(t, 123, 123, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
// cannot be determined and will always fail.
|
||||
func Equalf(t *testing.T, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return Equal(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotEqualf asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted")
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func NotEqualf(t *testing.T, expected interface{}, actual interface{}, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Nilf asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nilf(t, err, "error message %s", "formatted")
|
||||
func Nilf(t *testing.T, object interface{}, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return Nil(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotNilf asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNilf(t, err, "error message %s", "formatted")
|
||||
func NotNilf(t *testing.T, object interface{}, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return NotNil(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Emptyf asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Emptyf(t, obj, "error message %s", "formatted")
|
||||
func Emptyf(t *testing.T, object interface{}, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return Empty(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// NotEmptyf asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmptyf(t, obj, "error message %s", "formatted") {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmptyf(t *testing.T, object interface{}, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return NotEmpty(t, object, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
|
||||
// Lenf asserts that the specified object has specific length.
|
||||
// Lenf also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Lenf(t, mySlice, 3, "error message %s", "formatted")
|
||||
func Lenf(t *testing.T, object interface{}, length int, msg string, args ...interface{}) bool {
|
||||
t.Helper()
|
||||
return Len(t, object, length, append([]interface{}{msg}, args...)...)
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package assert
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Equal asserts that two objects are equal.
|
||||
//
|
||||
// assert.Equal(t, 123, 123)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses). Function equality
|
||||
// cannot be determined and will always fail.
|
||||
func Equal(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
if err := validateEqualArgs(expected, actual); err != nil {
|
||||
return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)",
|
||||
expected, actual, err), msgAndArgs...)
|
||||
}
|
||||
if !ObjectsAreEqual(expected, actual) {
|
||||
expected, actual = formatUnequalValues(expected, actual)
|
||||
return Fail(t, fmt.Sprintf("Not equal: \n"+
|
||||
"expected: %s\n"+
|
||||
"actual : %s", expected, actual), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NotEqual asserts that the specified values are NOT equal.
|
||||
//
|
||||
// assert.NotEqual(t, obj1, obj2)
|
||||
//
|
||||
// Pointer variable equality is determined based on the equality of the
|
||||
// referenced values (as opposed to the memory addresses).
|
||||
func NotEqual(t *testing.T, expected, actual interface{}, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
if err := validateEqualArgs(expected, actual); err != nil {
|
||||
return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)",
|
||||
expected, actual, err), msgAndArgs...)
|
||||
}
|
||||
if ObjectsAreEqual(expected, actual) {
|
||||
return Fail(t, fmt.Sprintf("Should not be: %#v\n", actual), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Empty asserts that the specified object is empty. I.e. nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// assert.Empty(t, obj)
|
||||
func Empty(t *testing.T, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := isEmpty(object)
|
||||
if !pass {
|
||||
t.Helper()
|
||||
Fail(t, fmt.Sprintf("Should be empty, but was %v", object), msgAndArgs...)
|
||||
}
|
||||
return pass
|
||||
}
|
||||
|
||||
// NotEmpty asserts that the specified object is NOT empty. I.e. not nil, "", false, 0 or either
|
||||
// a slice or a channel with len == 0.
|
||||
//
|
||||
// if assert.NotEmpty(t, obj) {
|
||||
// assert.Equal(t, "two", obj[1])
|
||||
// }
|
||||
func NotEmpty(t *testing.T, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
pass := !isEmpty(object)
|
||||
if !pass {
|
||||
t.Helper()
|
||||
Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...)
|
||||
}
|
||||
return pass
|
||||
}
|
||||
|
||||
// Nil asserts that the specified object is nil.
|
||||
//
|
||||
// assert.Nil(t, err)
|
||||
func Nil(t *testing.T, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if isNil(object) {
|
||||
return true
|
||||
}
|
||||
t.Helper()
|
||||
return Fail(t, fmt.Sprintf("Expected nil, but got: %#v", object), msgAndArgs...)
|
||||
}
|
||||
|
||||
// NotNil asserts that the specified object is not nil.
|
||||
//
|
||||
// assert.NotNil(t, err)
|
||||
func NotNil(t *testing.T, object interface{}, msgAndArgs ...interface{}) bool {
|
||||
if !isNil(object) {
|
||||
return true
|
||||
}
|
||||
t.Helper()
|
||||
return Fail(t, "Expected value not to be nil.", msgAndArgs...)
|
||||
}
|
||||
|
||||
// Len asserts that the specified object has specific length.
|
||||
// Len also fails if the object has a type that len() not accept.
|
||||
//
|
||||
// assert.Len(t, mySlice, 3)
|
||||
func Len(t *testing.T, object interface{}, length int, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
ok, l := getLen(object)
|
||||
if !ok {
|
||||
return Fail(t, fmt.Sprintf("\"%v\" could not be applied builtin len()", object), msgAndArgs...)
|
||||
}
|
||||
if l != length {
|
||||
return Fail(t, fmt.Sprintf("\"%s\" should have %d item(s), but has %d", object, length, l), msgAndArgs...)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Fail reports a failure through
|
||||
func Fail(t *testing.T, failureMessage string, msgAndArgs ...interface{}) bool {
|
||||
t.Helper()
|
||||
content := []labeledContent{
|
||||
{"Error", failureMessage},
|
||||
}
|
||||
message := messageFromMsgAndArgs(msgAndArgs...)
|
||||
if len(message) > 0 {
|
||||
content = append(content, labeledContent{"Messages", message})
|
||||
}
|
||||
t.Errorf("\n%s", ""+labeledOutput(content...))
|
||||
return false
|
||||
}
|
||||
|
||||
// ObjectsAreEqual determines if two objects are considered equal..
|
||||
func ObjectsAreEqual(expected, actual interface{}) bool {
|
||||
if expected == nil || actual == nil {
|
||||
return expected == actual
|
||||
}
|
||||
|
||||
exp, ok := expected.([]byte)
|
||||
if !ok {
|
||||
return reflect.DeepEqual(expected, actual)
|
||||
}
|
||||
|
||||
act, ok := actual.([]byte)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if exp == nil || act == nil {
|
||||
return exp == nil && act == nil
|
||||
}
|
||||
return bytes.Equal(exp, act)
|
||||
}
|
||||
|
||||
func validateEqualArgs(expected, actual interface{}) error {
|
||||
if expected == nil && actual == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if isFunction(expected) || isFunction(actual) {
|
||||
return errors.New("cannot take func type as argument")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func formatUnequalValues(expected, actual interface{}) (e string, a string) {
|
||||
if reflect.TypeOf(expected) != reflect.TypeOf(actual) {
|
||||
return fmt.Sprintf("%T(%s)", expected, truncatingFormat(expected)),
|
||||
fmt.Sprintf("%T(%s)", actual, truncatingFormat(actual))
|
||||
}
|
||||
switch expected.(type) {
|
||||
case time.Duration:
|
||||
return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual)
|
||||
}
|
||||
return truncatingFormat(expected), truncatingFormat(actual)
|
||||
}
|
||||
|
||||
func truncatingFormat(data interface{}) string {
|
||||
value := fmt.Sprintf("%#v", data)
|
||||
max := bufio.MaxScanTokenSize - 100 // Give us some space the type info too if needed.
|
||||
if len(value) > max {
|
||||
value = value[0:max] + "<... truncated>"
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
type labeledContent struct {
|
||||
label string
|
||||
content string
|
||||
}
|
||||
|
||||
func messageFromMsgAndArgs(msgAndArgs ...interface{}) string {
|
||||
if len(msgAndArgs) == 0 || msgAndArgs == nil {
|
||||
return ""
|
||||
}
|
||||
if len(msgAndArgs) == 1 {
|
||||
msg := msgAndArgs[0]
|
||||
if msgAsStr, ok := msg.(string); ok {
|
||||
return msgAsStr
|
||||
}
|
||||
return fmt.Sprintf("%+v", msg)
|
||||
}
|
||||
if len(msgAndArgs) > 1 {
|
||||
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func labeledOutput(content ...labeledContent) string {
|
||||
longestLabel := 0
|
||||
for _, v := range content {
|
||||
if len(v.label) > longestLabel {
|
||||
longestLabel = len(v.label)
|
||||
}
|
||||
}
|
||||
var output string
|
||||
for _, v := range content {
|
||||
output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n"
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func indentMessageLines(message string, longestLabelLen int) string {
|
||||
outBuf := new(bytes.Buffer)
|
||||
for i, scanner := 0, bufio.NewScanner(strings.NewReader(message)); scanner.Scan(); i++ {
|
||||
// no need to align first line because it starts at the correct location (after the label)
|
||||
if i != 0 {
|
||||
// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
|
||||
outBuf.WriteString("\n\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
|
||||
}
|
||||
outBuf.WriteString(scanner.Text())
|
||||
}
|
||||
return outBuf.String()
|
||||
}
|
||||
|
||||
func getLen(x interface{}) (ok bool, length int) {
|
||||
v := reflect.ValueOf(x)
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
return true, v.Len()
|
||||
}
|
||||
|
||||
func isFunction(arg interface{}) bool {
|
||||
if arg == nil {
|
||||
return false
|
||||
}
|
||||
return reflect.TypeOf(arg).Kind() == reflect.Func
|
||||
}
|
||||
|
||||
func isEmpty(object interface{}) bool {
|
||||
|
||||
if object == nil {
|
||||
return true
|
||||
}
|
||||
objValue := reflect.ValueOf(object)
|
||||
switch objValue.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:
|
||||
return objValue.Len() == 0
|
||||
case reflect.Ptr:
|
||||
if objValue.IsNil() {
|
||||
return true
|
||||
}
|
||||
deref := objValue.Elem().Interface()
|
||||
return isEmpty(deref)
|
||||
default:
|
||||
zero := reflect.Zero(objValue.Type())
|
||||
return reflect.DeepEqual(object, zero.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
func containsKind(kinds []reflect.Kind, kind reflect.Kind) bool {
|
||||
for i := 0; i < len(kinds); i++ {
|
||||
if kind == kinds[i] {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func isNil(object interface{}) bool {
|
||||
if object == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
value := reflect.ValueOf(object)
|
||||
kind := value.Kind()
|
||||
isNilableKind := containsKind(
|
||||
[]reflect.Kind{
|
||||
reflect.Chan, reflect.Func,
|
||||
reflect.Interface, reflect.Map,
|
||||
reflect.Ptr, reflect.Slice},
|
||||
kind)
|
||||
if isNilableKind && value.IsNil() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package byteutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"reflect"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// StringToBytes 强制转换 []byte(s)
|
||||
func StringToBytes(s string) []byte {
|
||||
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
|
||||
bh := reflect.SliceHeader{
|
||||
Data: sh.Data,
|
||||
Len: sh.Len,
|
||||
Cap: sh.Len,
|
||||
}
|
||||
return *(*[]byte)(unsafe.Pointer(&bh))
|
||||
}
|
||||
|
||||
// BytesToString 强制转换 string(s)
|
||||
func BytesToString(b []byte) string {
|
||||
return *(*string)(unsafe.Pointer(&b))
|
||||
}
|
||||
|
||||
// Uint64ToBytes uint64转byte
|
||||
func Uint64ToBytes(i uint64) []byte {
|
||||
var buf = make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(buf, i)
|
||||
return buf
|
||||
}
|
||||
|
||||
// BytesToUint64 byte转uint64
|
||||
func BytesToUint64(b []byte) uint64 {
|
||||
return binary.BigEndian.Uint64(b)
|
||||
}
|
||||
|
||||
// Split 数据分片
|
||||
func Split(buf []byte, limit int) [][]byte {
|
||||
var chunk []byte
|
||||
chunks := make([][]byte, 0, len(buf)/limit+1)
|
||||
for len(buf) >= limit {
|
||||
chunk, buf = buf[:limit], buf[limit:]
|
||||
chunks = append(chunks, chunk)
|
||||
}
|
||||
if len(buf) > 0 {
|
||||
chunks = append(chunks, buf[:])
|
||||
}
|
||||
return chunks
|
||||
}
|
||||
|
||||
// Join 数据合并
|
||||
func Join(s [][]byte) []byte {
|
||||
return bytes.Join(s, []byte(""))
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package byteutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringToBytes(t *testing.T) {
|
||||
x := "Hello Gopher!"
|
||||
y := StringToBytes(x)
|
||||
z := []byte(x)
|
||||
|
||||
if !bytes.Equal(y, z) {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesToString(t *testing.T) {
|
||||
x := []byte("Hello Gopher!")
|
||||
y := BytesToString(x)
|
||||
z := string(x)
|
||||
|
||||
if y != z {
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
|
||||
func TestUint64ToBytes(t *testing.T) {
|
||||
x := uint64(1234567890)
|
||||
y := Uint64ToBytes(x)
|
||||
t.Logf("Uint64ToBytes: %b", y)
|
||||
}
|
||||
|
||||
func TestBytesToUint64(t *testing.T) {
|
||||
x := uint64(1234567890)
|
||||
y := Uint64ToBytes(x)
|
||||
z := BytesToUint64(y)
|
||||
t.Logf("%d", z)
|
||||
}
|
||||
|
||||
func TestSplit(t *testing.T) {
|
||||
slice := Split([]byte("Hello Gopher!"), 1)
|
||||
t.Logf("%s", slice)
|
||||
}
|
||||
|
||||
func TestJoin(t *testing.T) {
|
||||
b := Join(Split([]byte("Hello Gopher!"), 1))
|
||||
t.Logf("%s", b)
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package convert
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// AnyToString ...
|
||||
func AnyToString(i interface{}) string {
|
||||
var s string
|
||||
switch v := i.(type) {
|
||||
case nil:
|
||||
s = ""
|
||||
case int:
|
||||
s = strconv.Itoa(v)
|
||||
case int8:
|
||||
s = strconv.Itoa(int(v))
|
||||
case int16:
|
||||
s = strconv.Itoa(int(v))
|
||||
case int32: // same as `rune`
|
||||
s = strconv.Itoa(int(v))
|
||||
case int64:
|
||||
s = strconv.Itoa(int(v))
|
||||
case uint:
|
||||
s = strconv.FormatUint(uint64(v), 10)
|
||||
case uint8:
|
||||
s = strconv.FormatUint(uint64(v), 10)
|
||||
case uint16:
|
||||
s = strconv.FormatUint(uint64(v), 10)
|
||||
case uint32:
|
||||
s = strconv.FormatUint(uint64(v), 10)
|
||||
case uint64:
|
||||
s = strconv.FormatUint(v, 10)
|
||||
case float32:
|
||||
s = strconv.FormatFloat(float64(v), 'f', -1, 32)
|
||||
case float64:
|
||||
s = strconv.FormatFloat(v, 'f', -1, 64)
|
||||
case bool:
|
||||
s = strconv.FormatBool(v)
|
||||
case string:
|
||||
s = v
|
||||
case []byte:
|
||||
s = string(v)
|
||||
case time.Duration:
|
||||
s = v.String()
|
||||
case json.Number:
|
||||
s = v.String()
|
||||
default:
|
||||
s = fmt.Sprint(v)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// IntToString int => string
|
||||
func IntToString(i int) string {
|
||||
return strconv.Itoa(i)
|
||||
}
|
||||
|
||||
// Uint64ToString uint64 => string
|
||||
func Uint64ToString(i uint64) string {
|
||||
return strconv.FormatUint(i, 10)
|
||||
}
|
||||
|
||||
// Float64ToString float64 => string
|
||||
func Float64ToString(f float64) string {
|
||||
return strconv.FormatFloat(f, 'f', -1, 64)
|
||||
}
|
||||
|
||||
// Float32ToString float32 => string
|
||||
func Float32ToString(f float32) string {
|
||||
return strconv.FormatFloat(float64(f), 'f', -1, 32)
|
||||
}
|
||||
|
||||
// StringToFloat64 string => float64
|
||||
func StringToFloat64(s string) float64 {
|
||||
f, _ := strconv.ParseFloat(s, 64)
|
||||
return f
|
||||
}
|
||||
|
||||
// StringToFloat32 string => float32
|
||||
func StringToFloat32(s string) float32 {
|
||||
f64, _ := strconv.ParseFloat(s, 32)
|
||||
return float32(f64)
|
||||
}
|
||||
|
||||
// StringToInt string => int
|
||||
func StringToInt(s string) int {
|
||||
i, _ := strconv.Atoi(s)
|
||||
return i
|
||||
}
|
||||
|
||||
// StringToInt32 string => int32
|
||||
func StringToInt32(s string) int32 {
|
||||
return int32(StringToInt64(s))
|
||||
}
|
||||
|
||||
// StringToInt64 string => int64
|
||||
func StringToInt64(s string) int64 {
|
||||
i, _ := strconv.ParseInt(s, 10, 64)
|
||||
return i
|
||||
}
|
||||
|
||||
// StringToUint64 string => uint64
|
||||
func StringToUint64(s string) uint64 {
|
||||
i, _ := strconv.ParseUint(s, 10, 64)
|
||||
return i
|
||||
}
|
||||
|
||||
// IntToUint int => uint
|
||||
func IntToUint(i int) uint {
|
||||
return uint(i)
|
||||
}
|
||||
|
||||
// UintToInt uint => int
|
||||
func UintToInt(i uint) int {
|
||||
return int(i)
|
||||
}
|
||||
|
||||
// JsonNumberToInt json.Number => int
|
||||
func JsonNumberToInt(n json.Number) int {
|
||||
i64, _ := n.Int64()
|
||||
return int(i64)
|
||||
}
|
||||
|
||||
// MapToJson map => json
|
||||
func MapToJson(m map[string]string) (string, error) {
|
||||
b, e := json.Marshal(m)
|
||||
if e != nil {
|
||||
return "", e
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// JsonToMap json => map
|
||||
func JsonToMap(s string) (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
err := json.Unmarshal([]byte(s), &m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Base64Encode base64 编码
|
||||
func Base64Encode(src []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(src)
|
||||
}
|
||||
|
||||
// Base64Decode base64 解码
|
||||
func Base64Decode(src string) ([]byte, error) {
|
||||
return base64.StdEncoding.DecodeString(src)
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package csvutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// WriteFile 追加写文件
|
||||
func WriteFile(fileName string, body [][]string, head []string) error {
|
||||
f, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := csv.NewWriter(f)
|
||||
w.Comma = ','
|
||||
w.UseCRLF = true
|
||||
if len(head) > 0 {
|
||||
r := csv.NewReader(f)
|
||||
var row []string
|
||||
if row, err = r.Read(); err != nil || len(row) == 0 {
|
||||
f.WriteString("\xEF\xBB\xBF")
|
||||
if err = w.Write(head); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err = w.WriteAll(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteBytes 写内存
|
||||
func WriteBytes(body [][]string, head []string) ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
w := csv.NewWriter(&buf)
|
||||
w.Comma = ','
|
||||
w.UseCRLF = true
|
||||
if len(head) > 0 {
|
||||
if buf.Len() == 0 {
|
||||
buf.WriteString("\xEF\xBB\xBF")
|
||||
if err := w.Write(head); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := w.WriteAll(body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// ReadFile 读文件
|
||||
func ReadFile(fileName string) ([][]string, []string, error) {
|
||||
f, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
r := csv.NewReader(f)
|
||||
content, err := r.ReadAll()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return content[1:], content[0], nil
|
||||
}
|
||||
|
||||
// ReadFileOffset 按行读取文件 offset >= 2
|
||||
func ReadFileOffset(fileName string, offset, limit int) ([][]string, []string, error) {
|
||||
f, err := os.Open(fileName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
r := csv.NewReader(f)
|
||||
body := make([][]string, 0)
|
||||
head := make([]string, 0)
|
||||
counter := 1
|
||||
for {
|
||||
var row []string
|
||||
if row, err = r.Read(); err != nil && err != io.EOF {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if counter == 1 {
|
||||
head = row
|
||||
} else if counter >= offset && counter < offset+limit {
|
||||
body = append(body, row)
|
||||
} else if counter >= offset+limit {
|
||||
break
|
||||
}
|
||||
counter++
|
||||
}
|
||||
return body, head, nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package csvutil
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestWriteFile(t *testing.T) {
|
||||
filePath := "testdata.csv"
|
||||
head := []string{"序号", "姓名", "电话"}
|
||||
body := make([][]string, 0)
|
||||
body = append(body, []string{"1", "Mark", "18812345678"}, []string{"2", "马克", "18612345678"})
|
||||
err := WriteFile(filePath, body, head)
|
||||
t.Logf("WriteFile: %v", err)
|
||||
}
|
||||
|
||||
func TestWriteBytes(t *testing.T) {
|
||||
head := []string{"序号", "姓名", "电话"}
|
||||
body := make([][]string, 0)
|
||||
body = append(body, []string{"1", "Mark", "18812345678"}, []string{"2", "马克", "18612345678"})
|
||||
b, _ := WriteBytes(body, head)
|
||||
t.Logf("WriteBytes: %s", b)
|
||||
}
|
||||
|
||||
func TestReadFile(t *testing.T) {
|
||||
filePath := "testdata.csv"
|
||||
body, head, _ := ReadFile(filePath)
|
||||
t.Logf("body: %s", body)
|
||||
t.Logf("head: %s", head)
|
||||
}
|
||||
|
||||
func TestReadFileOffset(t *testing.T) {
|
||||
filePath := "testdata.csv"
|
||||
body, head, err := ReadFileOffset(filePath, 4, 2)
|
||||
t.Logf("body: %s", body)
|
||||
t.Logf("head: %s", head)
|
||||
t.Logf("err: %v", err)
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package datetime
|
||||
|
||||
import "time"
|
||||
|
||||
const DefaultLayout string = "2006-01-02 15:04:05"
|
||||
const DayLayout string = "2006-01-02"
|
||||
|
||||
// Time 获取当前时间戳
|
||||
func Time() int64 {
|
||||
return time.Now().Unix()
|
||||
}
|
||||
|
||||
// MilliTime 获取当前毫秒时间戳
|
||||
func MilliTime() int64 {
|
||||
return time.Now().UnixNano() / 1e6
|
||||
}
|
||||
|
||||
// MicroTime 获取当前微秒时间戳
|
||||
func MicroTime() int64 {
|
||||
return time.Now().UnixNano() / 1e3
|
||||
}
|
||||
|
||||
// Date 时间戳格式化
|
||||
func Date(timestamp int64, layout string) string {
|
||||
return time.Unix(timestamp, 0).Format(layout)
|
||||
}
|
||||
|
||||
// Timestamp 时间转时间戳
|
||||
func Timestamp(datetime string, layout string) int64 {
|
||||
tm2, err := time.ParseInLocation(layout, datetime, time.Local)
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return tm2.Unix()
|
||||
}
|
||||
|
||||
// Datetime 获取当前时间
|
||||
func Datetime() string {
|
||||
return Date(Time(), DefaultLayout)
|
||||
}
|
||||
|
||||
// Today 获取今天日期
|
||||
func Today() string {
|
||||
return Date(Time(), DayLayout)
|
||||
}
|
||||
|
||||
// TodayStartTime Today => 00:00:00 获取今天起始时间戳
|
||||
func TodayStartTime() time.Time {
|
||||
return DayStartTime(time.Now())
|
||||
}
|
||||
|
||||
// TodayEndTime Today => 23:59:59 获取今天结束时间戳
|
||||
func TodayEndTime() time.Time {
|
||||
return DayEndTime(time.Now())
|
||||
}
|
||||
|
||||
// DayStartTime Day => 00:00:00 获取当天起始时间戳
|
||||
func DayStartTime(t time.Time) time.Time {
|
||||
tm2 := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local)
|
||||
return tm2
|
||||
}
|
||||
|
||||
// DayEndTime Day => 23:59:59 获取当天结束时间戳
|
||||
func DayEndTime(t time.Time) time.Time {
|
||||
tm2 := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, time.Local)
|
||||
return tm2
|
||||
}
|
||||
|
||||
// WeekStartTime Monday => 00:00:00 获取周起始时间戳
|
||||
func WeekStartTime(t time.Time) time.Time {
|
||||
offset := int(time.Monday - t.Weekday())
|
||||
if offset > 0 {
|
||||
offset = -6
|
||||
}
|
||||
tm2 := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.Local).
|
||||
AddDate(0, 0, offset)
|
||||
return tm2
|
||||
}
|
||||
|
||||
//WeekEndTime Sunday => 23:59:59 获取周结束时间戳
|
||||
func WeekEndTime(t time.Time) time.Time {
|
||||
offset := 0
|
||||
if w := t.Weekday(); w != 0 {
|
||||
offset = int(time.Saturday + 1 - w)
|
||||
}
|
||||
tm2 := time.Date(t.Year(), t.Month(), t.Day(), 23, 59, 59, 0, time.Local).
|
||||
AddDate(0, 0, offset)
|
||||
return tm2
|
||||
}
|
||||
|
||||
// MonthStartTime 月初 => 00:00:00 获取月起始时间戳
|
||||
func MonthStartTime(t time.Time) time.Time {
|
||||
tm2 := time.Date(t.Year(), t.Month(), 1, 0, 0, 0, 0, time.Local)
|
||||
return tm2
|
||||
}
|
||||
|
||||
// MonthEndTime 月末 => 23:59:59 获取月结束时间戳
|
||||
func MonthEndTime(t time.Time) time.Time {
|
||||
e := MonthStartTime(t).AddDate(0, 1, -1)
|
||||
tm2 := time.Date(t.Year(), t.Month(), e.Day(), 23, 59, 59, 0, time.Local)
|
||||
return tm2
|
||||
}
|
||||
|
||||
// YearStartTime 年初 => 00:00:00 获取年起始时间戳
|
||||
func YearStartTime(t time.Time) time.Time {
|
||||
tm2 := time.Date(t.Year(), time.January, 1, 0, 0, 0, 0, time.Local)
|
||||
return tm2
|
||||
}
|
||||
|
||||
// YearEndTime 年末 => 23:59:59 获取年起始时间戳
|
||||
func YearEndTime(t time.Time) time.Time {
|
||||
tm2 := time.Date(t.Year(), time.December, 31, 23, 59, 59, 0, time.Local)
|
||||
return tm2
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestTime(t *testing.T) {
|
||||
t.Logf("Time: %d", Time())
|
||||
}
|
||||
|
||||
func TestMilliTime(t *testing.T) {
|
||||
t.Logf("MilliTime: %d", MilliTime())
|
||||
}
|
||||
|
||||
func TestMicroTime(t *testing.T) {
|
||||
t.Logf("MicroTime: %d", MicroTime())
|
||||
}
|
||||
|
||||
func TestDateTime(t *testing.T) {
|
||||
t.Logf("Datetime: %s", Datetime())
|
||||
}
|
||||
|
||||
func TestTimestamp(t *testing.T) {
|
||||
datetime := "2021-06-06 11:11:11"
|
||||
t.Logf("Date: %d", Timestamp(datetime, DefaultLayout))
|
||||
}
|
||||
|
||||
func TestToday(t *testing.T) {
|
||||
t.Logf("Today: %s", Today())
|
||||
}
|
||||
|
||||
func TestTodayStartTime(t *testing.T) {
|
||||
t.Logf("TodayStartTime: %v", TodayStartTime())
|
||||
}
|
||||
|
||||
func TestTodayEndTime(t *testing.T) {
|
||||
t.Logf("TodayEndTime: %v", TodayEndTime())
|
||||
}
|
||||
|
||||
func TestDayStartTime(t *testing.T) {
|
||||
t.Logf("DayStartTime: %v", DayStartTime(time.Now()))
|
||||
}
|
||||
|
||||
func TestDayEndTime(t *testing.T) {
|
||||
t.Logf("DayEndTime: %v", DayEndTime(time.Now()))
|
||||
}
|
||||
|
||||
func TestWeekStartTime(t *testing.T) {
|
||||
t.Logf("WeekStartTime: %v", WeekStartTime(time.Now()))
|
||||
}
|
||||
|
||||
func TestWeekEndTime(t *testing.T) {
|
||||
t.Logf("WeekEndTime: %v", WeekEndTime(time.Now()))
|
||||
}
|
||||
|
||||
func TestMonthStartTime(t *testing.T) {
|
||||
t.Logf("MonthStartTime: %v", MonthStartTime(time.Now()))
|
||||
}
|
||||
|
||||
func TestMonthEndTime(t *testing.T) {
|
||||
t.Logf("MonthEndTime: %v", MonthEndTime(time.Now()))
|
||||
}
|
||||
func TestYearStartTime(t *testing.T) {
|
||||
t.Logf("YearStartTime: %v", YearStartTime(time.Now()))
|
||||
}
|
||||
|
||||
func TestYearEndTime(t *testing.T) {
|
||||
t.Logf("YearEndTime: %v", YearEndTime(time.Now()))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// SelfPath gets compiled executable file absolute path.
|
||||
func SelfPath() string {
|
||||
selfPath, _ := filepath.Abs(os.Args[0])
|
||||
return selfPath
|
||||
}
|
||||
|
||||
// SelfDir gets compiled executable file directory.
|
||||
func SelfDir() string {
|
||||
return filepath.Dir(SelfPath())
|
||||
}
|
||||
|
||||
//MkDir 创建目录.
|
||||
func MkDir(dirPath string) error {
|
||||
if IsExist(dirPath) {
|
||||
return nil
|
||||
}
|
||||
return os.MkdirAll(dirPath, os.ModePerm)
|
||||
}
|
||||
|
||||
// IsExist 判断文件或目录是否存在.
|
||||
func IsExist(filePath string) bool {
|
||||
_, err := os.Stat(filePath)
|
||||
return err == nil || os.IsExist(err)
|
||||
}
|
||||
|
||||
// IsEmpty 判断目录是否为空.
|
||||
func IsEmpty(dirname string) bool {
|
||||
dir, _ := ioutil.ReadDir(dirname)
|
||||
if len(dir) == 0 {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// IsFile 判断文件是否存在.
|
||||
func IsFile(filePath string) bool {
|
||||
f, e := os.Stat(filePath)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return !f.IsDir()
|
||||
}
|
||||
|
||||
// IsDir 判断目录是否存在.
|
||||
func IsDir(filePath string) bool {
|
||||
f, e := os.Stat(filePath)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
return f.IsDir()
|
||||
}
|
||||
|
||||
// ListIndex 目录下文件和子目录列表.
|
||||
func ListIndex(dirPath string) ([]string, []string, error) {
|
||||
if !IsDir(dirPath) {
|
||||
return nil, nil, errors.New(dirPath + " not a directory")
|
||||
}
|
||||
fs, err := ioutil.ReadDir(dirPath)
|
||||
var files, dirs []string
|
||||
for _, fi := range fs {
|
||||
if !fi.IsDir() {
|
||||
files = append(files, fi.Name())
|
||||
} else {
|
||||
dirs = append(dirs, fi.Name())
|
||||
}
|
||||
}
|
||||
return files, dirs, err
|
||||
}
|
||||
|
||||
// ClearDir 清空目录下所有文件不包括子目录.
|
||||
func ClearDir(dirPath string) error {
|
||||
dir, e := ioutil.ReadDir(dirPath)
|
||||
for _, d := range dir {
|
||||
if d.IsDir() {
|
||||
continue
|
||||
}
|
||||
_ = os.Remove(path.Join([]string{dirPath, d.Name()}...))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// ClearDirF 清空目录下所有文件和目录.
|
||||
func ClearDirF(dirPath string) error {
|
||||
dir, e := ioutil.ReadDir(dirPath)
|
||||
for _, d := range dir {
|
||||
_ = os.RemoveAll(path.Join([]string{dirPath, d.Name()}...))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
// RemoveDir 删除空目录.
|
||||
func RemoveDir(dirPath string) error {
|
||||
if !IsDir(dirPath) {
|
||||
return errors.New(dirPath + " not a directory")
|
||||
}
|
||||
return os.Remove(dirPath)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSelfPath(t *testing.T) {
|
||||
t.Logf("SelfPath: %v", SelfPath())
|
||||
}
|
||||
|
||||
func TestSelfDir(t *testing.T) {
|
||||
t.Logf("SelfDir: %v", SelfDir())
|
||||
}
|
||||
|
||||
func TestMkDir(t *testing.T) {
|
||||
dirPath := "testdata/dir/dir1"
|
||||
t.Logf("Mkdir: %v", MkDir(dirPath))
|
||||
}
|
||||
|
||||
func TestIsEmpty(t *testing.T) {
|
||||
dirPath := "testdata/dir"
|
||||
t.Logf("IsEmpty: %v", IsEmpty(dirPath))
|
||||
}
|
||||
|
||||
func TestIsDir(t *testing.T) {
|
||||
dirPath := "testdata/dir"
|
||||
t.Logf("IsDir: %v", IsDir(dirPath))
|
||||
}
|
||||
|
||||
func TestIsFile(t *testing.T) {
|
||||
dirPath := "testdata/dir"
|
||||
t.Logf("IsFile: %v", IsFile(dirPath))
|
||||
}
|
||||
|
||||
func TestListIndex(t *testing.T) {
|
||||
dirPath := "testdata"
|
||||
files, dirs, err := ListIndex(dirPath)
|
||||
t.Logf("ListFiles: %v, %v, %v", files, dirs, err)
|
||||
}
|
||||
|
||||
func TestClearDir(t *testing.T) {
|
||||
dirPath := "testdata/dir/dir1"
|
||||
t.Logf("ClearDir: %v", ClearDir(dirPath))
|
||||
}
|
||||
|
||||
func TestClearDirF(t *testing.T) {
|
||||
dirPath := "testdata"
|
||||
t.Logf("ClearDirF: %v", ClearDirF(dirPath))
|
||||
}
|
||||
|
||||
func TestRemoveDir(t *testing.T) {
|
||||
dirPath := "testdata"
|
||||
t.Logf("RemoveDir: %v", RemoveDir(dirPath))
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
/**
|
||||
os.O_WRONLY 只写
|
||||
os.O_CREATE 创建文件
|
||||
os.O_RDONLY 只读
|
||||
os.O_RDWR 读写
|
||||
os.O_TRUNC 清空
|
||||
os.O_APPEND 追加
|
||||
*/
|
||||
|
||||
// CreateFile 创建或清空文件
|
||||
func CreateFile(filePath string) (*os.File, error) {
|
||||
if err := MkDir(path.Dir(filePath)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return os.Create(filePath)
|
||||
}
|
||||
|
||||
// ReadFileToBytes 读文件
|
||||
func ReadFileToBytes(filePath string) ([]byte, error) {
|
||||
b, err := ioutil.ReadFile(filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteBytesToFile 写文件 覆盖
|
||||
func WriteBytesToFile(filePath string, b []byte) error {
|
||||
f, err := CreateFile(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
wt := bufio.NewWriter(f)
|
||||
_, err = io.Copy(wt, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wt.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReadHttpFileToBytes 读网络文件
|
||||
func ReadHttpFileToBytes(fileUrl string) ([]byte, error) {
|
||||
resp, err := http.Get(fileUrl)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
return ioutil.ReadAll(resp.Body)
|
||||
}
|
||||
|
||||
// Copy 复制文件
|
||||
func Copy(sourcePath, targetPath string) error {
|
||||
b, err := ReadFileToBytes(sourcePath)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return WriteBytesToFile(targetPath, b)
|
||||
}
|
||||
|
||||
// Download 下载文件
|
||||
func Download(sourceUrl, targetPath string) error {
|
||||
b, err := ReadHttpFileToBytes(sourceUrl)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return WriteBytesToFile(targetPath, b)
|
||||
}
|
||||
|
||||
// Name 获取文件名.
|
||||
func Name(filePath string) string {
|
||||
return filepath.Base(filePath)
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package fileutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCreateFile(t *testing.T) {
|
||||
filePath := "testdata/file/create.txt"
|
||||
fs, err := CreateFile(filePath)
|
||||
defer fs.Close()
|
||||
t.Logf("CreateFile: %v", err)
|
||||
}
|
||||
|
||||
func TestWriteBytesToFile(t *testing.T) {
|
||||
filePath := "testdata/file/write.txt"
|
||||
t.Logf("WriteBytesToFile: %v", WriteBytesToFile(filePath, []byte("hello")))
|
||||
}
|
||||
|
||||
func TestReadFileToBytes(t *testing.T) {
|
||||
filePath := "testdata/file/write.txt"
|
||||
b, err := ReadFileToBytes(filePath)
|
||||
t.Logf("ReadFileToBytes: %v, %v", string(b), err)
|
||||
}
|
||||
|
||||
func TestReadHttpFileToBytes(t *testing.T) {
|
||||
filePath := "https://game.gtimg.cn/images/yxzj/img201606/heroimg/109/109.jpg"
|
||||
b, err := ReadHttpFileToBytes(filePath)
|
||||
err = WriteBytesToFile("testdata/file/httpFile.jpeg", b)
|
||||
t.Logf("ReadHttpFileToBytes: %v", err)
|
||||
}
|
||||
|
||||
func TestCopy(t *testing.T) {
|
||||
sourcePath := "testdata/file/write.txt"
|
||||
targetPath := "testdata/file/target.txt"
|
||||
t.Logf("Copy: %v", Copy(sourcePath, targetPath))
|
||||
}
|
||||
|
||||
func TestDownload(t *testing.T) {
|
||||
sourceUrl := "https://game.gtimg.cn/images/yxzj/img201606/heroimg/109/109.jpg"
|
||||
targetPath := "testdata/file/109-download.jpeg"
|
||||
t.Logf("Copy: %v", Download(sourceUrl, targetPath))
|
||||
}
|
||||
|
||||
func TestName(t *testing.T) {
|
||||
t.Logf("Name: %v", Name("testdata/file/109-download.jpeg"))
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package hashutil
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/sha1"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"hash"
|
||||
)
|
||||
|
||||
// Md5 hashes using md5 algorithm
|
||||
func Md5(b []byte) []byte {
|
||||
return Hashes(md5.New(), b)
|
||||
}
|
||||
|
||||
// Md5Hex hashes using md5 algorithm
|
||||
func Md5Hex(text string) string {
|
||||
return StringHashes(md5.New(), text)
|
||||
}
|
||||
|
||||
// Sha1 hashes using sha1 algorithm
|
||||
func Sha1(b []byte) []byte {
|
||||
return Hashes(sha1.New(), b)
|
||||
}
|
||||
|
||||
// Sha1Hex hashes using sha1 algorithm
|
||||
func Sha1Hex(text string) string {
|
||||
return StringHashes(sha1.New(), text)
|
||||
}
|
||||
|
||||
// Sha256 hashes using sha256 algorithm
|
||||
func Sha256(b []byte) []byte {
|
||||
return Hashes(sha256.New(), b)
|
||||
}
|
||||
|
||||
// Sha256Hex hashes using sha256 algorithm
|
||||
func Sha256Hex(text string) string {
|
||||
return StringHashes(sha256.New(), text)
|
||||
}
|
||||
|
||||
// Sha512 hashes using sha512 algorithm
|
||||
func Sha512(b []byte) []byte {
|
||||
return Hashes(sha512.New(), b)
|
||||
}
|
||||
|
||||
// Sha512Hex hashes using sha512 algorithm
|
||||
func Sha512Hex(text string) string {
|
||||
return StringHashes(sha512.New(), text)
|
||||
}
|
||||
|
||||
// HmacMd5 hashes using md5 algorithm with a secret
|
||||
func HmacMd5(b []byte, secret []byte) []byte {
|
||||
algorithm := hmac.New(md5.New, secret)
|
||||
return Hashes(algorithm, b)
|
||||
}
|
||||
|
||||
// HmacMd5Hex hashes using md5 algorithm with a secret
|
||||
func HmacMd5Hex(text, secret string) string {
|
||||
return StringHashes(hmac.New(md5.New, []byte(secret)), text)
|
||||
}
|
||||
|
||||
// HmacSha256 hashes using sha256 algorithm with a secret
|
||||
func HmacSha256(b []byte, secret []byte) []byte {
|
||||
return Hashes(hmac.New(sha256.New, secret), b)
|
||||
}
|
||||
|
||||
// HmacSha256Hex hashes using sha256 algorithm with a secret
|
||||
func HmacSha256Hex(text, secret string) string {
|
||||
return StringHashes(hmac.New(sha256.New, []byte(secret)), text)
|
||||
}
|
||||
|
||||
// HmacSha512 hashes using sha512 algorithm with a secret
|
||||
func HmacSha512(b []byte, secret []byte) []byte {
|
||||
return Hashes(hmac.New(sha512.New, secret), b)
|
||||
}
|
||||
|
||||
// HmacSha512Hex hashes using sha512 algorithm with a secret
|
||||
func HmacSha512Hex(text, secret string) string {
|
||||
return StringHashes(hmac.New(sha512.New, []byte(secret)), text)
|
||||
}
|
||||
|
||||
// StringHashes hashes string
|
||||
func StringHashes(algorithm hash.Hash, text string) string {
|
||||
return hex.EncodeToString(Hashes(algorithm, []byte(text)))
|
||||
}
|
||||
|
||||
// Hashes hashes
|
||||
func Hashes(algorithm hash.Hash, b []byte) []byte {
|
||||
algorithm.Write(b)
|
||||
return algorithm.Sum(nil)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package httputil
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func PostWithHeader(url string, msg []byte, headers map[string]string) (string, error) {
|
||||
client := &http.Client{}
|
||||
|
||||
req, err := http.NewRequest("POST", url, strings.NewReader(string(msg)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for key, header := range headers {
|
||||
req.Header.Set(key, header)
|
||||
}
|
||||
resp, err := client.Do(req)
|
||||
defer resp.Body.Close()
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(body), nil
|
||||
}
|
||||
|
||||
func PostWithAuthorization(url, authorization string, msg []byte) (string, error) {
|
||||
headers := make(map[string]string)
|
||||
headers["Authorization"] = authorization
|
||||
headers["Content-Type"] = "application/json"
|
||||
return PostWithHeader(url, msg, headers)
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package maputil
|
||||
|
||||
import "strings"
|
||||
|
||||
// IsEmpty ...
|
||||
func IsEmpty(mp map[string]string) bool {
|
||||
return len(mp) == 0
|
||||
}
|
||||
|
||||
// HasKey ...
|
||||
func HasKey(mp map[string]string, key string) bool {
|
||||
if _, ok := mp[key]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Value ...
|
||||
func Value(mp map[string]string, key string) string {
|
||||
if HasKey(mp, key) {
|
||||
return mp[key]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// HasValue ...
|
||||
func HasValue(mp map[string]string, value string) bool {
|
||||
for _, v := range mp {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Keys ...
|
||||
func Keys(mp map[string]string) []string {
|
||||
ks := make([]string, 0, len(mp))
|
||||
for k, _ := range mp {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
return ks
|
||||
}
|
||||
|
||||
// Values ...
|
||||
func Values(mp map[string]string) []string {
|
||||
vs := make([]string, 0, len(mp))
|
||||
for _, v := range mp {
|
||||
vs = append(vs, v)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// KeyToLower convert keys to lower case.
|
||||
func KeyToLower(mp map[string]string) map[string]string {
|
||||
nmp := make(map[string]string, len(mp))
|
||||
for k, v := range mp {
|
||||
k = strings.ToLower(k)
|
||||
nmp[k] = v
|
||||
}
|
||||
return nmp
|
||||
}
|
||||
|
||||
// Merge src 会覆盖 dst
|
||||
func Merge(src, dst map[string]string) map[string]string {
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package mask
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Mask 自定义字符脱敏 保留前f位和最后e位
|
||||
func Mask(s string, f, e int) string {
|
||||
l := len(s)
|
||||
if f+e >= l || f < 0 || e < 0 {
|
||||
return ""
|
||||
}
|
||||
return s[0:f] + strings.Repeat("*", l-f-e) + s[l-e:]
|
||||
}
|
||||
|
||||
// Left 保留前f位
|
||||
func Left(s string, f int) string {
|
||||
return Mask(s, f, 0)
|
||||
}
|
||||
|
||||
// Right 保留后e位
|
||||
func Right(s string, e int) string {
|
||||
return Mask(s, 0, e)
|
||||
}
|
||||
|
||||
// First 取值前f位
|
||||
func First(s string, f int) string {
|
||||
l := len(s)
|
||||
if l <= f || f < 0 {
|
||||
return s
|
||||
}
|
||||
return s[0:f]
|
||||
}
|
||||
|
||||
// Last 取值后e位
|
||||
func Last(s string, e int) string {
|
||||
l := len(s)
|
||||
if l <= e || e < 0 {
|
||||
return s
|
||||
}
|
||||
return s[l-e:]
|
||||
}
|
||||
|
||||
// LastFour 后四位
|
||||
func LastFour(s string) string {
|
||||
return Last(s, 4)
|
||||
}
|
||||
|
||||
// IdCard 身份证号脱敏
|
||||
func IdCard(s string) string {
|
||||
if len(s) != 18 {
|
||||
return ""
|
||||
}
|
||||
return s[0:4] + " **** **** " + s[len(s)-4:]
|
||||
}
|
||||
|
||||
// IdCardStrict 严格身份证号脱敏
|
||||
func IdCardStrict(s string) string {
|
||||
if len(s) != 18 {
|
||||
return ""
|
||||
}
|
||||
return s[0:1] + "*** **** **** ***" + s[len(s)-1:]
|
||||
}
|
||||
|
||||
// Mobile 手机号脱敏
|
||||
func Mobile(s string) string {
|
||||
return s[0:3] + "****" + s[len(s)-4:]
|
||||
}
|
||||
|
||||
// ChineseName 中文姓名脱敏
|
||||
func ChineseName(s string) string {
|
||||
r := []rune(s)
|
||||
l := len(r)
|
||||
if l == 2 {
|
||||
return "*" + string(r[1:])
|
||||
} else if l == 3 {
|
||||
return "*" + string(r[l-2:])
|
||||
} else if l == 4 {
|
||||
return "**" + string(r[l-2:])
|
||||
} else if l > 4 {
|
||||
return string(r[:1]) + strings.Repeat("*", l-3) + string(r[l-2:])
|
||||
}
|
||||
return "**"
|
||||
}
|
||||
|
||||
// Email 邮箱脱敏
|
||||
func Email(s string) string {
|
||||
ss := strings.Split(s, "@")
|
||||
l := len(ss[0])
|
||||
if l <= 1 {
|
||||
return "*@" + ss[1]
|
||||
}
|
||||
r := []rune(s)
|
||||
return string(r[0:1]) + strings.Repeat("*", l-1) + "@" + ss[1]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package mathutil
|
||||
|
||||
// MaxInt64 ...
|
||||
func MaxInt64(is ...int64) int64 {
|
||||
max := is[0]
|
||||
for _, v := range is {
|
||||
if v > max {
|
||||
max = v
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
// MinInt64 ...
|
||||
func MinInt64(is ...int64) int64 {
|
||||
min := is[0]
|
||||
for _, v := range is {
|
||||
if v < min {
|
||||
min = v
|
||||
}
|
||||
}
|
||||
return min
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package random
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
randSeek = int64(1)
|
||||
l sync.Mutex
|
||||
)
|
||||
|
||||
// GetRandomInt 生成值小于max的随机数
|
||||
func GetRandomInt(max int) int {
|
||||
rand.Seed(getRandSeek())
|
||||
return rand.Intn(max)
|
||||
}
|
||||
|
||||
// GetRandomChars 生成英文字母随机字符串
|
||||
func GetRandomChars(num int) string {
|
||||
ss := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
return GetRandomString(num, ss)
|
||||
}
|
||||
|
||||
// GetRandomNumbers 生成数字类型随机字符串
|
||||
func GetRandomNumbers(num int) string {
|
||||
return GetRandomString(num)
|
||||
}
|
||||
|
||||
// GetRandomString 生成随机字符串
|
||||
func GetRandomString(num int, str ...string) string {
|
||||
s := "0123456789"
|
||||
if len(str) > 0 {
|
||||
s = str[0]
|
||||
}
|
||||
l := len(s)
|
||||
r := rand.New(rand.NewSource(getRandSeek()))
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < num; i++ {
|
||||
x := r.Intn(l)
|
||||
buf.WriteString(s[x : x+1])
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func getRandSeek() int64 {
|
||||
l.Lock()
|
||||
if randSeek >= 100000000 {
|
||||
randSeek = 1
|
||||
}
|
||||
randSeek++
|
||||
l.Unlock()
|
||||
return time.Now().UnixNano() + randSeek
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package random
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetRandomSring(t *testing.T) {
|
||||
t.Logf("GetRandomString: %s", GetRandomString(4, "abide123456"))
|
||||
t.Logf("GetRandomString: %s", GetRandomString(6, "abcdefghijklmnopqrstuvwxyz0123456789"))
|
||||
}
|
||||
|
||||
func TestGetRandomChars(t *testing.T) {
|
||||
t.Logf("GetRandomChars: %s", GetRandomChars(4))
|
||||
t.Logf("GetRandomChars: %s", GetRandomChars(6))
|
||||
}
|
||||
|
||||
func TestGetRandomNumbers(t *testing.T) {
|
||||
t.Logf("GetRandomNumbers: %s", GetRandomNumbers(4))
|
||||
t.Logf("GetRandomNumbers: %s", GetRandomNumbers(6))
|
||||
}
|
||||
|
||||
func TestGetRandomInt(t *testing.T) {
|
||||
t.Logf("GetRandomInt: %d", GetRandomInt(10))
|
||||
t.Logf("GetRandomInt: %d", GetRandomInt(10000))
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package rsautil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// EncryptToBase64 ...
|
||||
func EncryptToBase64(plainText []byte, base64PubKey string) (string, error) {
|
||||
publicBytes, err := base64.StdEncoding.DecodeString(base64PubKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cipherBytes, err := encrypt(plainText, publicBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(cipherBytes), nil
|
||||
}
|
||||
|
||||
// DecryptByBase64 ...
|
||||
func DecryptByBase64(cipherText, base64PriKey string) ([]byte, error) {
|
||||
privateBytes, err := base64.StdEncoding.DecodeString(base64PriKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipherBytes, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decrypt(cipherBytes, privateBytes)
|
||||
}
|
||||
|
||||
// EncryptToHex ...
|
||||
func EncryptToHex(plainText []byte, hexPubKey string) (string, error) {
|
||||
publicBytes, err := hex.DecodeString(hexPubKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cipherBytes, err := encrypt(plainText, publicBytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(cipherBytes), nil
|
||||
}
|
||||
|
||||
// DecryptByHex ...
|
||||
func DecryptByHex(cipherText, hexPriKey string) ([]byte, error) {
|
||||
privateBytes, err := hex.DecodeString(hexPriKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipherTextBytes, err := hex.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return decrypt(cipherTextBytes, privateBytes)
|
||||
}
|
||||
|
||||
// encrypt 加密
|
||||
func encrypt(plainText, pubKey []byte) ([]byte, error) {
|
||||
pub, err := x509.ParsePKIXPublicKey(pubKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
publicKey := pub.(*rsa.PublicKey)
|
||||
pubSize, plainTextSize := publicKey.Size(), len(plainText)
|
||||
// EncryptPKCS1v15 encrypts the given message with RSA and the padding
|
||||
// scheme from PKCS #1 v1.5. The message must be no longer than the
|
||||
// length of the public modulus minus 11 bytes.
|
||||
//
|
||||
// The rand parameter is used as a source of entropy to ensure that
|
||||
// encrypting the same message twice doesn't result in the same
|
||||
// ciphertext.
|
||||
//
|
||||
// WARNING: use of this function to encrypt plaintexts other than
|
||||
// session keys is dangerous. Use RSA OAEP in new protocols.
|
||||
offSet, once := 0, pubSize-11
|
||||
buffer := bytes.Buffer{}
|
||||
for offSet < plainTextSize {
|
||||
endIndex := offSet + once
|
||||
if endIndex > plainTextSize {
|
||||
endIndex = plainTextSize
|
||||
}
|
||||
bytesOnce, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, plainText[offSet:endIndex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffer.Write(bytesOnce)
|
||||
offSet = endIndex
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// decrypt 解密
|
||||
func decrypt(cipherText, priKey []byte) (plainText []byte, err error) {
|
||||
pri, err := x509.ParsePKCS8PrivateKey(priKey)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
privateKey := pri.(*rsa.PrivateKey)
|
||||
priSize, cipherTextSize := privateKey.Size(), len(cipherText)
|
||||
var offSet = 0
|
||||
var buffer = bytes.Buffer{}
|
||||
for offSet < cipherTextSize {
|
||||
endIndex := offSet + priSize
|
||||
if endIndex > cipherTextSize {
|
||||
endIndex = cipherTextSize
|
||||
}
|
||||
bytesOnce, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, cipherText[offSet:endIndex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buffer.Write(bytesOnce)
|
||||
offSet = endIndex
|
||||
}
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package rsautil
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
)
|
||||
|
||||
var (
|
||||
RSABitsErr = errors.New("bits 1024 or 2048")
|
||||
RSAPrivateKeyPemDecodeErr = errors.New("private key pem.decode error")
|
||||
RSAPublicKeyPemDecodeErr = errors.New("public key pem.decode error")
|
||||
)
|
||||
|
||||
type RsaKey struct {
|
||||
PrivateKey string // PKCS#8
|
||||
PublicKey string // PKCS#8
|
||||
}
|
||||
|
||||
// GenerateRsaKey 生成公钥私钥
|
||||
func GenerateRsaKey(bits int) ([]byte, []byte, error) {
|
||||
if bits != 1024 && bits != 2048 {
|
||||
return nil, nil, RSABitsErr
|
||||
}
|
||||
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
priKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
pubKey, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return priKey, pubKey, nil
|
||||
}
|
||||
|
||||
// GenerateRsaKeyBase64 生成公钥私钥 Base64
|
||||
func GenerateRsaKeyBase64(bits int) (RsaKey, error) {
|
||||
priKey, pubKey, err := GenerateRsaKey(bits)
|
||||
if err != nil {
|
||||
return RsaKey{}, err
|
||||
}
|
||||
return RsaKey{
|
||||
PrivateKey: base64.StdEncoding.EncodeToString(priKey),
|
||||
PublicKey: base64.StdEncoding.EncodeToString(pubKey),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GenerateRsaKeyHex 生成公钥私钥 Hex
|
||||
func GenerateRsaKeyHex(bits int) (RsaKey, error) {
|
||||
priKey, pubKey, err := GenerateRsaKey(bits)
|
||||
if err != nil {
|
||||
return RsaKey{}, err
|
||||
}
|
||||
return RsaKey{
|
||||
PrivateKey: hex.EncodeToString(priKey),
|
||||
PublicKey: hex.EncodeToString(pubKey),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package sliceutil
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// InSlice 判断字符串是否存在
|
||||
func InSlice(value string, ss []string) bool {
|
||||
for _, v := range ss {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsEmpty 判断Slice是否为空
|
||||
func IsEmpty(ss []string) bool {
|
||||
if len(ss) == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Implode 别名 strings.Join
|
||||
func Implode(sep string, ss ...string) string {
|
||||
return strings.Join(ss, sep)
|
||||
}
|
||||
|
||||
// Explode 别名 strings.Split
|
||||
func Explode(sep string, s string) []string {
|
||||
return strings.Split(s, sep)
|
||||
}
|
||||
|
||||
// Unique slice去重
|
||||
func Unique(ss []string) []string {
|
||||
l := len(ss)
|
||||
// 无法保障顺序
|
||||
m := make(map[string]bool)
|
||||
for i := 0; i < l; i++ {
|
||||
m[ss[i]] = true
|
||||
}
|
||||
|
||||
nl := len(m)
|
||||
n := make([]string, nl)
|
||||
|
||||
i := 0
|
||||
for v := range m {
|
||||
n[i] = v
|
||||
i++
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
// Merge slice合并 - 不去重
|
||||
func Merge(slice1, slice2 []string) []string {
|
||||
n := make([]string, len(slice1)+len(slice2))
|
||||
copy(n, slice1)
|
||||
copy(n[len(slice1):], slice2)
|
||||
return n
|
||||
}
|
||||
|
||||
// Intersect slice交集
|
||||
func Intersect(slice1, slice2 []string) []string {
|
||||
m := make(map[string]int)
|
||||
n := make([]string, 0)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 1 {
|
||||
n = append(n, v)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Union slice并集
|
||||
func Union(slice1, slice2 []string) []string {
|
||||
m := make(map[string]int)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 0 {
|
||||
slice1 = append(slice1, v)
|
||||
}
|
||||
}
|
||||
return slice1
|
||||
}
|
||||
|
||||
// Difference slice差集 - 属于slice1,不属于slice2
|
||||
func Difference(slice1, slice2 []string) []string {
|
||||
m := make(map[string]int)
|
||||
n := make([]string, 0)
|
||||
inter := Intersect(slice1, slice2)
|
||||
for _, v := range inter {
|
||||
m[v]++
|
||||
}
|
||||
|
||||
for _, value := range slice1 {
|
||||
times, _ := m[value]
|
||||
if times == 0 {
|
||||
n = append(n, value)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// IntersectUint64 slice交集
|
||||
func IntersectUint64(slice1, slice2 []uint64) []uint64 {
|
||||
m := make(map[uint64]int)
|
||||
n := make([]uint64, 0)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 1 {
|
||||
n = append(n, v)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// UnionUint64 slice并集
|
||||
func UnionUint64(slice1, slice2 []uint64) []uint64 {
|
||||
m := make(map[uint64]int)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 0 {
|
||||
slice1 = append(slice1, v)
|
||||
}
|
||||
}
|
||||
return slice1
|
||||
}
|
||||
|
||||
// DifferenceUint64 slice差集 - 属于slice1,不属于slice2
|
||||
func DifferenceUint64(slice1, slice2 []uint64) []uint64 {
|
||||
m := make(map[uint64]int)
|
||||
n := make([]uint64, 0)
|
||||
inter := IntersectUint64(slice1, slice2)
|
||||
for _, v := range inter {
|
||||
m[v]++
|
||||
}
|
||||
|
||||
for _, value := range slice1 {
|
||||
times, _ := m[value]
|
||||
if times == 0 {
|
||||
n = append(n, value)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// IntersectInterface slice交集
|
||||
func IntersectInterface(slice1, slice2 []interface{}) []interface{} {
|
||||
m := make(map[interface{}]int)
|
||||
n := make([]interface{}, 0)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 1 {
|
||||
n = append(n, v)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// UnionInterface slice并集
|
||||
func UnionInterface(slice1, slice2 []interface{}) []interface{} {
|
||||
m := make(map[interface{}]int)
|
||||
for _, v := range slice1 {
|
||||
m[v]++
|
||||
}
|
||||
for _, v := range slice2 {
|
||||
times, _ := m[v]
|
||||
if times == 0 {
|
||||
slice1 = append(slice1, v)
|
||||
}
|
||||
}
|
||||
return slice1
|
||||
}
|
||||
|
||||
// DifferenceInterface slice差集 - 属于slice1,不属于slice2
|
||||
func DifferenceInterface(slice1, slice2 []interface{}) []interface{} {
|
||||
m := make(map[interface{}]int)
|
||||
n := make([]interface{}, 0)
|
||||
inter := IntersectInterface(slice1, slice2)
|
||||
for _, v := range inter {
|
||||
m[v]++
|
||||
}
|
||||
|
||||
for _, value := range slice1 {
|
||||
times, _ := m[value]
|
||||
if times == 0 {
|
||||
n = append(n, value)
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
|
||||
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
package stringutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Reverse 反转字符串
|
||||
func Reverse(s string) string {
|
||||
r := []rune(s)
|
||||
for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
|
||||
r[i], r[j] = r[j], r[i]
|
||||
}
|
||||
return string(r)
|
||||
}
|
||||
|
||||
// UcFirst 首字母大写
|
||||
func UcFirst(s string) string {
|
||||
for i, v := range s {
|
||||
return string(unicode.ToUpper(v)) + s[i+1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// LcFirst 首字母小写
|
||||
func LcFirst(s string) string {
|
||||
for i, v := range s {
|
||||
return string(unicode.ToLower(v)) + s[i+1:]
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// CamelToSnake camel => snake 简单实现
|
||||
func CamelToSnake(s string) string {
|
||||
buffer := new(bytes.Buffer)
|
||||
for i, r := range s {
|
||||
if unicode.IsUpper(r) {
|
||||
if i != 0 {
|
||||
buffer.WriteRune('_')
|
||||
}
|
||||
buffer.WriteRune(unicode.ToLower(r))
|
||||
} else {
|
||||
buffer.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// SnakeToCamel snake => camel 简单实现
|
||||
func SnakeToCamel(s string) string {
|
||||
s = strings.Replace(s, "_", " ", -1)
|
||||
s = strings.Title(s)
|
||||
return strings.Replace(s, " ", "", -1)
|
||||
}
|
||||
|
||||
func SnakeToSpinal(s string) string {
|
||||
return strings.Replace(s, "_", "-", -1)
|
||||
}
|
||||
|
||||
func SpinalToSnake(s string) string {
|
||||
return strings.Replace(s, "-", "_", -1)
|
||||
}
|
||||
|
||||
// UrlEncode 空格被编码为+,+被编码为%2B
|
||||
func UrlEncode(s string) string {
|
||||
return url.QueryEscape(s)
|
||||
}
|
||||
|
||||
// UrlDecode URL解码
|
||||
func UrlDecode(s string) string {
|
||||
u, _ := url.QueryUnescape(s)
|
||||
return u
|
||||
}
|
||||
|
||||
// Substr 字符串切割
|
||||
func Substr(s string, pos, length int) string {
|
||||
r := []rune(s)
|
||||
sl := len(r)
|
||||
if pos >= sl {
|
||||
return ""
|
||||
}
|
||||
idx := pos + length
|
||||
if length == 0 || idx > sl {
|
||||
idx = sl
|
||||
} else if length < 0 {
|
||||
idx = sl + length
|
||||
}
|
||||
|
||||
return string(r[pos:idx])
|
||||
}
|
||||
|
||||
// InString 判断子字符串是否存在
|
||||
func InString(sub, str string) bool {
|
||||
if str != "" && strings.Contains(str, sub) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RegexpReplace ...
|
||||
func RegexpReplace(src, expr, repl string) (string, error) {
|
||||
reg, err := regexp.Compile(expr)
|
||||
return reg.ReplaceAllString(src, repl), err
|
||||
}
|
||||
|
||||
// TrimSpace 去除字符串前后空格、换行等
|
||||
func TrimSpace(s string) string {
|
||||
s = strings.TrimSpace(s)
|
||||
s = strings.Trim(s, "\r")
|
||||
s = strings.Trim(s, "\n")
|
||||
s = strings.Trim(s, "\t")
|
||||
return s
|
||||
}
|
||||
Reference in new issue
Block a user