goref/value.go

161 lines
3.3 KiB
Go

package goref
import (
"fmt"
"reflect"
"strings"
)
func ToString(val any) (string, bool) {
vo := reflect.ValueOf(val)
if vo.Kind() == reflect.Ptr {
vo = vo.Elem()
}
if vo.Kind() == reflect.String {
return vo.String(), true
}
return "", false
}
func ToStrings(val any) ([]string, bool) {
vo := reflect.ValueOf(val)
if vo.Kind() == reflect.Ptr {
vo = vo.Elem()
}
if vo.Kind() == reflect.Slice || vo.Kind() == reflect.Array {
var list []string
for i := 0; i < vo.Len(); i++ {
val := vo.Index(i).Interface()
valVo := reflect.ValueOf(val)
if valVo.Kind() == reflect.Ptr {
valVo = valVo.Elem()
}
if valVo.Kind() == reflect.String {
list = append(list, valVo.String())
}
}
return list, true
}
return nil, false
}
func ToInterfaces(val any) ([]interface{}, bool) {
vo := reflect.ValueOf(val)
if vo.Kind() == reflect.Ptr {
vo = vo.Elem()
}
if vo.Len() == 0 {
return nil, false
}
var result []interface{}
for i := 0; i < vo.Len(); i++ {
result = append(result, vo.Index(i).Interface())
}
return result, true
}
type tagger struct {
tag string
omitempty bool
}
func (t *tagger) merge() {
if t.tag == "" {
t.tag = "-"
}
}
type taggerOption func(*tagger)
func WithTag(tag string) taggerOption {
return func(tager *tagger) {
tager.tag = tag
}
}
func WithOmitempty(omit bool) taggerOption {
return func(tager *tagger) {
tager.omitempty = omit
}
}
func StructToMap(val any, tagOpts ...taggerOption) (map[string]interface{}, error) {
vo := reflect.ValueOf(val)
if vo.Kind() == reflect.Ptr {
vo = vo.Elem()
}
if vo.Kind() != reflect.Struct {
return nil, fmt.Errorf("val type not struct")
}
var data = make(map[string]interface{})
var tagger = new(tagger)
for _, opt := range tagOpts {
opt(tagger)
}
tagger.merge()
for i := 0; i < vo.NumField(); i++ {
vf := vo.Field(i)
if tagger.omitempty && vf.IsZero() {
continue
}
key := vo.Type().Field(i).Tag.Get(tagger.tag)
if key == "" {
key = vo.Type().Field(i).Name
}
if strings.Contains(key, ",omitempty") {
key = strings.Replace(key, ",omitempty", "", 1)
}
data[key] = vf.Interface()
}
return data, nil
}
// SliceStructToMap slice struct to map, field as key
// value type []struct, bind type *map[string]*struct
func SliceStructToMap(value, bind any, field string) error {
// bind type check
if GetListBaseType(value) != GetMapValueBaseType(bind) {
return fmt.Errorf("struct type no match map type")
}
vo := reflect.ValueOf(value)
if vo.Kind() == reflect.Ptr {
vo = vo.Elem()
}
if vo.Kind() != reflect.Slice && vo.Kind() != reflect.Array {
return fmt.Errorf("val type not array or slice: " + vo.String())
}
bo := reflect.ValueOf(bind)
if bo.Kind() == reflect.Ptr {
bo = bo.Elem()
}
if bo.Kind() != reflect.Map {
return fmt.Errorf("invalid map type")
}
if !bo.CanSet() {
return fmt.Errorf("bind map must be initialized")
}
boKey := bo.Type().Key()
boVal := bo.Type().Elem()
bo.Set(reflect.MakeMap(reflect.MapOf(boKey, boVal)))
for i := 0; i < vo.Len(); i++ {
t := vo.Index(i)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
idxField := t.FieldByName(field)
if idxField.Kind() == reflect.Invalid {
continue
}
idxVal := vo.Index(i)
if boVal.Kind() == reflect.Ptr {
bo.SetMapIndex(idxField, idxVal.Addr())
} else {
bo.SetMapIndex(idxField, idxVal)
}
}
return nil
}