mirror of
https://github.com/duke-git/lancet.git
synced 2026-02-04 12:52:28 +08:00
350 lines
8.7 KiB
Go
350 lines
8.7 KiB
Go
// Copyright 2021 dudaodong@gmail.com. All rights reserved.
|
|
// Use of this source code is governed by MIT license
|
|
|
|
// Package convertor implements some functions to convert data.
|
|
package convertor
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"encoding/gob"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/duke-git/lancet/v2/structs"
|
|
)
|
|
|
|
// ToBool convert string to boolean.
|
|
// Play: https://go.dev/play/p/ARht2WnGdIN
|
|
func ToBool(s string) (bool, error) {
|
|
return strconv.ParseBool(s)
|
|
}
|
|
|
|
// ToBytes convert value to byte slice.
|
|
// Play: https://go.dev/play/p/fAMXYFDvOvr
|
|
func ToBytes(value any) ([]byte, error) {
|
|
v := reflect.ValueOf(value)
|
|
|
|
switch value.(type) {
|
|
case int, int8, int16, int32, int64:
|
|
number := v.Int()
|
|
buf := bytes.NewBuffer([]byte{})
|
|
buf.Reset()
|
|
err := binary.Write(buf, binary.BigEndian, number)
|
|
return buf.Bytes(), err
|
|
case uint, uint8, uint16, uint32, uint64:
|
|
number := v.Uint()
|
|
buf := bytes.NewBuffer([]byte{})
|
|
buf.Reset()
|
|
err := binary.Write(buf, binary.BigEndian, number)
|
|
return buf.Bytes(), err
|
|
case float32:
|
|
number := float32(v.Float())
|
|
bits := math.Float32bits(number)
|
|
bytes := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(bytes, bits)
|
|
return bytes, nil
|
|
case float64:
|
|
number := v.Float()
|
|
bits := math.Float64bits(number)
|
|
bytes := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(bytes, bits)
|
|
return bytes, nil
|
|
case bool:
|
|
return strconv.AppendBool([]byte{}, v.Bool()), nil
|
|
case string:
|
|
return []byte(v.String()), nil
|
|
case []byte:
|
|
return v.Bytes(), nil
|
|
default:
|
|
newValue, err := json.Marshal(value)
|
|
return newValue, err
|
|
}
|
|
}
|
|
|
|
// ToChar convert string to char slice.
|
|
// Play: https://go.dev/play/p/JJ1SvbFkVdM
|
|
func ToChar(s string) []string {
|
|
c := make([]string, 0)
|
|
if len(s) == 0 {
|
|
c = append(c, "")
|
|
}
|
|
for _, v := range s {
|
|
c = append(c, string(v))
|
|
}
|
|
return c
|
|
}
|
|
|
|
// ToChannel convert a slice of elements to a read-only channel.
|
|
// Play: https://go.dev/play/p/hOx_oYZbAnL
|
|
func ToChannel[T any](array []T) <-chan T {
|
|
ch := make(chan T)
|
|
|
|
go func() {
|
|
for _, item := range array {
|
|
ch <- item
|
|
}
|
|
close(ch)
|
|
}()
|
|
|
|
return ch
|
|
}
|
|
|
|
// ToString convert value to string
|
|
// for number, string, []byte, will convert to string
|
|
// for other type (slice, map, array, struct) will call json.Marshal.
|
|
// Play: https://go.dev/play/p/nF1zOOslpQq
|
|
func ToString(value any) string {
|
|
if value == nil {
|
|
return ""
|
|
}
|
|
|
|
switch val := value.(type) {
|
|
case float32:
|
|
return strconv.FormatFloat(float64(val), 'f', -1, 32)
|
|
case float64:
|
|
return strconv.FormatFloat(val, 'f', -1, 64)
|
|
case int:
|
|
return strconv.FormatInt(int64(val), 10)
|
|
case int8:
|
|
return strconv.FormatInt(int64(val), 10)
|
|
case int16:
|
|
return strconv.FormatInt(int64(val), 10)
|
|
case int32:
|
|
return strconv.FormatInt(int64(val), 10)
|
|
case int64:
|
|
return strconv.FormatInt(val, 10)
|
|
case uint:
|
|
return strconv.FormatUint(uint64(val), 10)
|
|
case uint8:
|
|
return strconv.FormatUint(uint64(val), 10)
|
|
case uint16:
|
|
return strconv.FormatUint(uint64(val), 10)
|
|
case uint32:
|
|
return strconv.FormatUint(uint64(val), 10)
|
|
case uint64:
|
|
return strconv.FormatUint(val, 10)
|
|
case string:
|
|
return val
|
|
case []byte:
|
|
return string(val)
|
|
default:
|
|
b, err := json.Marshal(val)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
return string(b)
|
|
|
|
// todo: maybe we should't supprt other type conversion
|
|
// v := reflect.ValueOf(value)
|
|
// log.Panicf("Unsupported data type: %s ", v.String())
|
|
// return ""
|
|
}
|
|
}
|
|
|
|
// ToJson convert value to a json string.
|
|
// Play: https://go.dev/play/p/2rLIkMmXWvR
|
|
func ToJson(value any) (string, error) {
|
|
result, err := json.Marshal(value)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(result), nil
|
|
}
|
|
|
|
// ToFloat convert value to float64, if input is not a float return 0.0 and error.
|
|
// Play: https://go.dev/play/p/4YTmPCibqHJ
|
|
func ToFloat(value any) (float64, error) {
|
|
v := reflect.ValueOf(value)
|
|
|
|
result := 0.0
|
|
err := fmt.Errorf("ToInt: unvalid interface type %T", value)
|
|
switch value.(type) {
|
|
case int, int8, int16, int32, int64:
|
|
result = float64(v.Int())
|
|
return result, nil
|
|
case uint, uint8, uint16, uint32, uint64:
|
|
result = float64(v.Uint())
|
|
return result, nil
|
|
case float32, float64:
|
|
result = v.Float()
|
|
return result, nil
|
|
case string:
|
|
result, err = strconv.ParseFloat(v.String(), 64)
|
|
if err != nil {
|
|
result = 0.0
|
|
}
|
|
return result, err
|
|
default:
|
|
return result, err
|
|
}
|
|
}
|
|
|
|
// ToInt convert value to int64 value, if input is not numerical, return 0 and error.
|
|
// Play: https://go.dev/play/p/9_h9vIt-QZ_b
|
|
func ToInt(value any) (int64, error) {
|
|
v := reflect.ValueOf(value)
|
|
|
|
var result int64
|
|
err := fmt.Errorf("ToInt: invalid value type %T", value)
|
|
switch value.(type) {
|
|
case int, int8, int16, int32, int64:
|
|
result = v.Int()
|
|
return result, nil
|
|
case uint, uint8, uint16, uint32, uint64:
|
|
result = int64(v.Uint())
|
|
return result, nil
|
|
case float32, float64:
|
|
result = int64(v.Float())
|
|
return result, nil
|
|
case string:
|
|
result, err = strconv.ParseInt(v.String(), 0, 64)
|
|
if err != nil {
|
|
result = 0
|
|
}
|
|
return result, err
|
|
default:
|
|
return result, err
|
|
}
|
|
}
|
|
|
|
// ToPointer returns a pointer to passed value.
|
|
// Play: https://go.dev/play/p/ASf_etHNlw1
|
|
func ToPointer[T any](value T) *T {
|
|
return &value
|
|
}
|
|
|
|
// ToMap convert a slice of structs to a map based on iteratee function.
|
|
// Play: https://go.dev/play/p/tVFy7E-t24l
|
|
func ToMap[T any, K comparable, V any](array []T, iteratee func(T) (K, V)) map[K]V {
|
|
result := make(map[K]V, len(array))
|
|
for _, item := range array {
|
|
k, v := iteratee(item)
|
|
result[k] = v
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// StructToMap convert struct to map, only convert exported struct field
|
|
// map key is specified same as struct field tag `json` value.
|
|
// Play: https://go.dev/play/p/KYGYJqNUBOI
|
|
func StructToMap(value any) (map[string]any, error) {
|
|
return structs.ToMap(value)
|
|
}
|
|
|
|
// MapToSlice convert map to slice based on iteratee function.
|
|
// Play: https://go.dev/play/p/dmX4Ix5V6Wl
|
|
func MapToSlice[T any, K comparable, V any](aMap map[K]V, iteratee func(K, V) T) []T {
|
|
result := make([]T, 0, len(aMap))
|
|
|
|
for k, v := range aMap {
|
|
result = append(result, iteratee(k, v))
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// ColorHexToRGB convert hex color to rgb color.
|
|
// Play: https://go.dev/play/p/o7_ft-JCJBV
|
|
func ColorHexToRGB(colorHex string) (red, green, blue int) {
|
|
colorHex = strings.TrimPrefix(colorHex, "#")
|
|
color64, err := strconv.ParseInt(colorHex, 16, 32)
|
|
if err != nil {
|
|
return
|
|
}
|
|
color := int(color64)
|
|
return color >> 16, (color & 0x00FF00) >> 8, color & 0x0000FF
|
|
}
|
|
|
|
// ColorRGBToHex convert rgb color to hex color.
|
|
// Play: https://go.dev/play/p/nzKS2Ro87J1
|
|
func ColorRGBToHex(red, green, blue int) string {
|
|
r := strconv.FormatInt(int64(red), 16)
|
|
g := strconv.FormatInt(int64(green), 16)
|
|
b := strconv.FormatInt(int64(blue), 16)
|
|
|
|
if len(r) == 1 {
|
|
r = "0" + r
|
|
}
|
|
if len(g) == 1 {
|
|
g = "0" + g
|
|
}
|
|
if len(b) == 1 {
|
|
b = "0" + b
|
|
}
|
|
|
|
return "#" + r + g + b
|
|
}
|
|
|
|
// EncodeByte encode data to byte slice.
|
|
// Play: https://go.dev/play/p/DVmM1G5JfuP
|
|
func EncodeByte(data any) ([]byte, error) {
|
|
buffer := bytes.NewBuffer(nil)
|
|
encoder := gob.NewEncoder(buffer)
|
|
err := encoder.Encode(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return buffer.Bytes(), nil
|
|
}
|
|
|
|
// DecodeByte decode byte slice data to target object.
|
|
// Play: https://go.dev/play/p/zI6xsmuQRbn
|
|
func DecodeByte(data []byte, target any) error {
|
|
buffer := bytes.NewBuffer(data)
|
|
decoder := gob.NewDecoder(buffer)
|
|
return decoder.Decode(target)
|
|
}
|
|
|
|
// DeepClone creates a deep copy of passed item.
|
|
// can't clone unexported field of struct
|
|
// Play: https://go.dev/play/p/j4DP5dquxnk
|
|
func DeepClone[T any](src T) T {
|
|
c := cloner{
|
|
ptrs: map[reflect.Type]map[uintptr]reflect.Value{},
|
|
}
|
|
result := c.clone(reflect.ValueOf(src))
|
|
if result.Kind() == reflect.Invalid {
|
|
var zeroValue T
|
|
return zeroValue
|
|
}
|
|
|
|
return result.Interface().(T)
|
|
}
|
|
|
|
// CopyProperties copies each field from the source into the destination. It recursively copies struct pointers and interfaces that contain struct pointers.
|
|
// use json.Marshal/Unmarshal, so json tag should be set for fields of dst and src struct.
|
|
// Play: https://go.dev/play/p/oZujoB5Sgg5
|
|
func CopyProperties[T, U any](dst T, src U) error {
|
|
dstType, srcType := reflect.TypeOf(dst), reflect.TypeOf(src)
|
|
|
|
if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
|
|
return errors.New("CopyProperties: parameter dst should be struct pointer")
|
|
}
|
|
|
|
if srcType.Kind() == reflect.Ptr {
|
|
srcType = srcType.Elem()
|
|
}
|
|
if srcType.Kind() != reflect.Struct {
|
|
return errors.New("CopyProperties: parameter src should be a struct or struct pointer")
|
|
}
|
|
|
|
bytes, err := json.Marshal(src)
|
|
if err != nil {
|
|
return fmt.Errorf("CopyProperties: unable to marshal src: %s", err)
|
|
}
|
|
err = json.Unmarshal(bytes, dst)
|
|
if err != nil {
|
|
return fmt.Errorf("CopyProperties: unable to unmarshal into dst: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|