Bump github.com/gookit/config/v2 from 2.1.8 to 2.2.2
Bumps [github.com/gookit/config/v2](https://github.com/gookit/config) from 2.1.8 to 2.2.2. - [Release notes](https://github.com/gookit/config/releases) - [Commits](https://github.com/gookit/config/compare/v2.1.8...v2.2.2) --- updated-dependencies: - dependency-name: github.com/gookit/config/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
03045c5ccc
commit
5ebc596352
+89
-21
@@ -2,7 +2,11 @@ package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gookit/goutil/maputil"
|
||||
"github.com/gookit/goutil/reflects"
|
||||
)
|
||||
|
||||
// ToMap quickly convert structs to map by reflect
|
||||
@@ -25,16 +29,73 @@ func TryToMap(st any, optFns ...MapOptFunc) (map[string]any, error) {
|
||||
return StructToMap(st, optFns...)
|
||||
}
|
||||
|
||||
// ToSMap quickly and safe convert structs to map[string]string by reflect
|
||||
func ToSMap(st any, optFns ...MapOptFunc) map[string]string {
|
||||
mp, _ := StructToMap(st, optFns...)
|
||||
return maputil.ToStringMap(mp)
|
||||
}
|
||||
|
||||
// TryToSMap quickly convert structs to map[string]string by reflect
|
||||
func TryToSMap(st any, optFns ...MapOptFunc) (map[string]string, error) {
|
||||
mp, err := StructToMap(st, optFns...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return maputil.ToStringMap(mp), nil
|
||||
}
|
||||
|
||||
// MustToSMap alias of ToStringMap(), but will panic on error
|
||||
func MustToSMap(st any, optFns ...MapOptFunc) map[string]string {
|
||||
mp, err := StructToMap(st, optFns...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return maputil.ToStringMap(mp)
|
||||
}
|
||||
|
||||
// ToString quickly format struct to string
|
||||
func ToString(st any, optFns ...MapOptFunc) string {
|
||||
mp, err := StructToMap(st, optFns...)
|
||||
if err == nil {
|
||||
return maputil.ToString(mp)
|
||||
}
|
||||
return fmt.Sprint(st)
|
||||
}
|
||||
|
||||
const defaultFieldTag = "json"
|
||||
|
||||
// MapOptions struct
|
||||
// MapOptions for convert struct to map
|
||||
type MapOptions struct {
|
||||
// TagName for map filed. default is "json"
|
||||
TagName string
|
||||
// ParseDepth for parse. TODO support depth
|
||||
ParseDepth int
|
||||
// MergeAnonymous struct fields to parent map. default is true
|
||||
MergeAnonymous bool
|
||||
// ExportPrivate export private fields. default is false
|
||||
ExportPrivate bool
|
||||
}
|
||||
|
||||
// MapOptFunc define
|
||||
type MapOptFunc func(opt *MapOptions)
|
||||
|
||||
// WithMapTagName set tag name for map field
|
||||
func WithMapTagName(tagName string) MapOptFunc {
|
||||
return func(opt *MapOptions) {
|
||||
opt.TagName = tagName
|
||||
}
|
||||
}
|
||||
|
||||
// MergeAnonymous merge anonymous struct fields to parent map
|
||||
func MergeAnonymous(opt *MapOptions) {
|
||||
opt.MergeAnonymous = true
|
||||
}
|
||||
|
||||
// ExportPrivate merge anonymous struct fields to parent map
|
||||
func ExportPrivate(opt *MapOptions) {
|
||||
opt.ExportPrivate = true
|
||||
}
|
||||
|
||||
// StructToMap quickly convert structs to map[string]any by reflect.
|
||||
// Can custom export field name by tag `json` or custom tag
|
||||
func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) {
|
||||
@@ -43,13 +104,9 @@ func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) {
|
||||
return mp, nil
|
||||
}
|
||||
|
||||
obj := reflect.ValueOf(st)
|
||||
if obj.Kind() == reflect.Ptr {
|
||||
obj = obj.Elem()
|
||||
}
|
||||
|
||||
obj := reflect.Indirect(reflect.ValueOf(st))
|
||||
if obj.Kind() != reflect.Struct {
|
||||
return mp, errors.New("must be an struct")
|
||||
return mp, errors.New("must be an struct value")
|
||||
}
|
||||
|
||||
opt := &MapOptions{TagName: defaultFieldTag}
|
||||
@@ -57,23 +114,25 @@ func StructToMap(st any, optFns ...MapOptFunc) (map[string]any, error) {
|
||||
fn(opt)
|
||||
}
|
||||
|
||||
mp, err := structToMap(obj, opt.TagName)
|
||||
_, err := structToMap(obj, opt, mp)
|
||||
return mp, err
|
||||
}
|
||||
|
||||
func structToMap(obj reflect.Value, tagName string) (map[string]any, error) {
|
||||
refType := obj.Type()
|
||||
mp := make(map[string]any)
|
||||
func structToMap(obj reflect.Value, opt *MapOptions, mp map[string]any) (map[string]any, error) {
|
||||
if mp == nil {
|
||||
mp = make(map[string]any)
|
||||
}
|
||||
|
||||
refType := obj.Type()
|
||||
for i := 0; i < obj.NumField(); i++ {
|
||||
ft := refType.Field(i)
|
||||
name := ft.Name
|
||||
// skip don't exported field
|
||||
if name[0] >= 'a' && name[0] <= 'z' {
|
||||
// skip un-exported field
|
||||
if !opt.ExportPrivate && IsUnexported(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
tagVal, ok := ft.Tag.Lookup(tagName)
|
||||
tagVal, ok := ft.Tag.Lookup(opt.TagName)
|
||||
if ok && tagVal != "" {
|
||||
sMap, err := ParseTagValueDefault(name, tagVal)
|
||||
if err != nil {
|
||||
@@ -81,24 +140,33 @@ func structToMap(obj reflect.Value, tagName string) (map[string]any, error) {
|
||||
}
|
||||
|
||||
name = sMap.Default("name", name)
|
||||
// un-exported field
|
||||
if name == "" {
|
||||
if name == "" { // un-exported field
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
field := obj.Field(i)
|
||||
field := reflect.Indirect(obj.Field(i))
|
||||
if field.Kind() == reflect.Struct {
|
||||
sub, err := structToMap(field, tagName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// collect anonymous struct values to parent.
|
||||
if ft.Anonymous && opt.MergeAnonymous {
|
||||
_, err := structToMap(field, opt, mp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else { // collect struct values to submap
|
||||
sub, err := structToMap(field, opt, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mp[name] = sub
|
||||
}
|
||||
mp[name] = sub
|
||||
continue
|
||||
}
|
||||
|
||||
if field.CanInterface() {
|
||||
mp[name] = field.Interface()
|
||||
} else if field.CanAddr() { // for unexported field
|
||||
mp[name] = reflects.UnexportedValue(field)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package structs
|
||||
|
||||
// MapStruct simple copy src struct value to dst struct
|
||||
// func MapStruct(srcSt, dstSt any) {
|
||||
// // TODO
|
||||
// }
|
||||
+19
@@ -173,3 +173,22 @@ func (d *Data) BoolVal(key string) bool {
|
||||
func (d *Data) String() string {
|
||||
return maputil.ToString(d.data)
|
||||
}
|
||||
|
||||
// OrderedMap data TODO
|
||||
type OrderedMap struct {
|
||||
maputil.Data
|
||||
len int
|
||||
keys []string
|
||||
// vals []any
|
||||
}
|
||||
|
||||
// NewOrderedMap instance.
|
||||
func NewOrderedMap(len int) *OrderedMap {
|
||||
return &OrderedMap{len: len}
|
||||
}
|
||||
|
||||
// Set key and value to map
|
||||
func (om *OrderedMap) Set(key string, val any) {
|
||||
om.keys = append(om.keys, key)
|
||||
om.Data.Set(key, val)
|
||||
}
|
||||
|
||||
+179
@@ -0,0 +1,179 @@
|
||||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/reflects"
|
||||
"github.com/gookit/goutil/strutil"
|
||||
)
|
||||
|
||||
const defaultInitTag = "default"
|
||||
|
||||
// InitOptFunc define
|
||||
type InitOptFunc func(opt *InitOptions)
|
||||
|
||||
// InitOptions struct
|
||||
type InitOptions struct {
|
||||
// TagName default value tag name. tag: default
|
||||
TagName string
|
||||
// ParseEnv var name on default value. eg: `default:"${APP_ENV}"`
|
||||
//
|
||||
// default: false
|
||||
ParseEnv bool
|
||||
// ValueHook before set value hook TODO
|
||||
ValueHook func(val string) any
|
||||
}
|
||||
|
||||
// Init struct default value by field "default" tag.
|
||||
func Init(ptr any, optFns ...InitOptFunc) error {
|
||||
return InitDefaults(ptr, optFns...)
|
||||
}
|
||||
|
||||
// InitDefaults init struct default value by field "default" tag.
|
||||
//
|
||||
// TIPS:
|
||||
//
|
||||
// Support init field types: string, bool, intX, uintX, floatX, array, slice
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type User1 struct {
|
||||
// Name string `default:"inhere"`
|
||||
// Age int32 `default:"30"`
|
||||
// }
|
||||
//
|
||||
// u1 := &User1{}
|
||||
// err = structs.InitDefaults(u1)
|
||||
// fmt.Printf("%+v\n", u1) // Output: {Name:inhere Age:30}
|
||||
func InitDefaults(ptr any, optFns ...InitOptFunc) error {
|
||||
rv := reflect.ValueOf(ptr)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
return errors.New("must be provider an pointer value")
|
||||
}
|
||||
|
||||
rv = rv.Elem()
|
||||
if rv.Kind() != reflect.Struct {
|
||||
return errors.New("must be provider an struct value")
|
||||
}
|
||||
|
||||
opt := &InitOptions{TagName: defaultInitTag}
|
||||
for _, fn := range optFns {
|
||||
fn(opt)
|
||||
}
|
||||
|
||||
return initDefaults(rv, opt)
|
||||
}
|
||||
|
||||
func initDefaults(rv reflect.Value, opt *InitOptions) error {
|
||||
rt := rv.Type()
|
||||
|
||||
for i := 0; i < rt.NumField(); i++ {
|
||||
sf := rt.Field(i)
|
||||
// skip don't exported field
|
||||
if IsUnexported(sf.Name) {
|
||||
continue
|
||||
}
|
||||
|
||||
val, hasTag := sf.Tag.Lookup(opt.TagName)
|
||||
if !hasTag || val == "-" {
|
||||
continue
|
||||
}
|
||||
|
||||
fv := rv.Field(i)
|
||||
if fv.Kind() == reflect.Struct {
|
||||
if err := initDefaults(fv, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// skip on field has value
|
||||
if !fv.IsZero() {
|
||||
// special: handle for pointer struct field
|
||||
if fv.Kind() == reflect.Pointer {
|
||||
fv = fv.Elem()
|
||||
if fv.Kind() == reflect.Struct {
|
||||
if err := initDefaults(fv, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// handle for pointer field
|
||||
if fv.Kind() == reflect.Pointer {
|
||||
if fv.IsNil() {
|
||||
fv.Set(reflect.New(fv.Type().Elem()))
|
||||
}
|
||||
|
||||
fv = fv.Elem()
|
||||
if fv.Kind() == reflect.Struct {
|
||||
if err := initDefaults(fv, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
} else if fv.Kind() == reflect.Slice {
|
||||
el := sf.Type.Elem()
|
||||
isPtr := el.Kind() == reflect.Pointer
|
||||
if isPtr {
|
||||
el = el.Elem()
|
||||
}
|
||||
|
||||
// init sub struct in slice. like `[]SubStruct` or `[]*SubStruct`
|
||||
if el.Kind() == reflect.Struct {
|
||||
// make sub-struct and init. like: `SubStruct`
|
||||
subFv := reflect.New(el)
|
||||
subFvE := subFv.Elem()
|
||||
if err := initDefaults(subFvE, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// make new slice and set value.
|
||||
newFv := reflect.MakeSlice(reflect.SliceOf(sf.Type.Elem()), 0, 1)
|
||||
if isPtr {
|
||||
newFv = reflect.Append(newFv, subFv)
|
||||
} else {
|
||||
newFv = reflect.Append(newFv, subFvE)
|
||||
}
|
||||
fv.Set(newFv)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := initDefaultValue(fv, val, opt.ParseEnv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initDefaultValue(fv reflect.Value, val string, parseEnv bool) error {
|
||||
if val == "" || !fv.CanSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse env var
|
||||
if parseEnv {
|
||||
val = comfunc.ParseEnvVar(val, nil)
|
||||
}
|
||||
|
||||
var anyVal any = val
|
||||
|
||||
// simple slice: convert simple kind(string,intX,uintX,...) to slice. eg: "1,2,3" => []int{1,2,3}
|
||||
if reflects.IsArrayOrSlice(fv.Kind()) && reflects.IsSimpleKind(reflects.SliceElemKind(fv.Type())) {
|
||||
ss := strutil.SplitTrimmed(val, ",")
|
||||
valRv, err := reflects.ConvSlice(reflect.ValueOf(ss), fv.Type().Elem())
|
||||
if err == nil {
|
||||
reflects.SetRValue(fv, valRv)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// set value
|
||||
return reflects.SetValue(fv, anyVal)
|
||||
}
|
||||
+12
-4
@@ -1,7 +1,15 @@
|
||||
// Package structs Provide some extends util functions for struct. eg: tag parse, struct init, value set
|
||||
package structs
|
||||
|
||||
// MapStruct simple copy src struct value to dst struct
|
||||
// func MapStruct(srcSt, dstSt any) {
|
||||
// // TODO
|
||||
// }
|
||||
// IsExported field name on struct
|
||||
func IsExported(name string) bool {
|
||||
return name[0] >= 'A' && name[0] <= 'Z'
|
||||
}
|
||||
|
||||
// IsUnexported field name on struct
|
||||
func IsUnexported(name string) bool {
|
||||
if name[0] == '_' {
|
||||
return true
|
||||
}
|
||||
return name[0] >= 'a' && name[0] <= 'z'
|
||||
}
|
||||
|
||||
+14
-3
@@ -204,6 +204,14 @@ func ParseTagValueDefault(field, tagVal string) (mp maputil.SMap, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// ParseTagValueQuick quick parse tag value string by sep(;)
|
||||
func ParseTagValueQuick(tagVal string, defines []string) maputil.SMap {
|
||||
parseFn := ParseTagValueDefine(";", defines)
|
||||
|
||||
mp, _ := parseFn("", tagVal)
|
||||
return mp
|
||||
}
|
||||
|
||||
// ParseTagValueDefine parse tag value string by given defines.
|
||||
//
|
||||
// Examples:
|
||||
@@ -235,7 +243,11 @@ func ParseTagValueDefine(sep string, defines []string) TagValFunc {
|
||||
|
||||
// ParseTagValueNamed parse k-v tag value string. it's like INI format contents.
|
||||
//
|
||||
// eg: "name=int0;shorts=i;required=true;desc=int option message"
|
||||
// Examples:
|
||||
//
|
||||
// eg: "name=val0;shorts=i;required=true;desc=a message"
|
||||
// =>
|
||||
// {name: val0, shorts: i, required: true, desc: a message}
|
||||
func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap, err error) {
|
||||
ss := strutil.Split(tagVal, ";")
|
||||
ln := len(ss)
|
||||
@@ -250,8 +262,7 @@ func ParseTagValueNamed(field, tagVal string, keys ...string) (mp maputil.SMap,
|
||||
return
|
||||
}
|
||||
|
||||
kvNodes := strings.SplitN(s, "=", 2)
|
||||
key, val := kvNodes[0], strings.TrimSpace(kvNodes[1])
|
||||
key, val := strutil.TrimCut(s, "=")
|
||||
if len(keys) > 0 && !arrutil.StringsHas(keys, key) {
|
||||
err = fmt.Errorf("parse tag error on field '%s': invalid key name '%s'", field, key)
|
||||
return
|
||||
|
||||
+7
-2
@@ -35,6 +35,11 @@ func (v *Value) Val() any {
|
||||
return v.V
|
||||
}
|
||||
|
||||
// Val get
|
||||
// func (v *Value) ValOr[T any](defVal T) T {
|
||||
// return v.V
|
||||
// }
|
||||
|
||||
// Int value get
|
||||
func (v *Value) Int() int {
|
||||
if v.V == nil {
|
||||
@@ -102,7 +107,7 @@ func (v *Value) Strings() (ss []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// SplitToStrings split string value to strings
|
||||
// SplitToStrings split string value to strings. sep default is comma(,)
|
||||
func (v *Value) SplitToStrings(sep ...string) (ss []string) {
|
||||
if v.V == nil {
|
||||
return
|
||||
@@ -114,7 +119,7 @@ func (v *Value) SplitToStrings(sep ...string) (ss []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// SplitToInts split string value to []int
|
||||
// SplitToInts split string value to []int. sep default is comma(,)
|
||||
func (v *Value) SplitToInts(sep ...string) (ss []int) {
|
||||
if v.V == nil {
|
||||
return
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package structs
|
||||
|
||||
import "reflect"
|
||||
|
||||
// Wrapper struct for read or set field value TODO
|
||||
type Wrapper struct {
|
||||
// src any // source data struct
|
||||
rv reflect.Value
|
||||
|
||||
// FieldTagName field name for read/write value. default tag: json
|
||||
FieldTagName string
|
||||
}
|
||||
|
||||
// Wrap create a struct wrapper
|
||||
func Wrap(src any) *Wrapper {
|
||||
return NewWrapper(src)
|
||||
}
|
||||
|
||||
// NewWrapper create a struct wrapper
|
||||
func NewWrapper(src any) *Wrapper {
|
||||
return WrapValue(reflect.ValueOf(src))
|
||||
}
|
||||
|
||||
// WrapValue create a struct wrapper
|
||||
func WrapValue(rv reflect.Value) *Wrapper {
|
||||
rv = reflect.Indirect(rv)
|
||||
if rv.Kind() != reflect.Struct {
|
||||
panic("must be provider an struct value")
|
||||
}
|
||||
|
||||
return &Wrapper{rv: rv}
|
||||
}
|
||||
|
||||
// Get field value by name
|
||||
func (r *Wrapper) Get(name string) any {
|
||||
val, ok := r.Lookup(name)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// Lookup field value by name
|
||||
func (r *Wrapper) Lookup(name string) (val any, ok bool) {
|
||||
fv := r.rv.FieldByName(name)
|
||||
if !fv.IsValid() {
|
||||
return
|
||||
}
|
||||
|
||||
return fv.Interface(), true
|
||||
}
|
||||
Generated
Vendored
+29
-105
@@ -5,111 +5,22 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/gookit/goutil/internal/comfunc"
|
||||
"github.com/gookit/goutil/maputil"
|
||||
"github.com/gookit/goutil/reflects"
|
||||
)
|
||||
|
||||
const defaultInitTag = "default"
|
||||
|
||||
// InitOptFunc define
|
||||
type InitOptFunc func(opt *InitOptions)
|
||||
|
||||
// InitOptions struct
|
||||
type InitOptions struct {
|
||||
// TagName default value tag name. tag: default
|
||||
TagName string
|
||||
// ParseEnv var name on default value. eg: `default:"${APP_ENV}"`
|
||||
//
|
||||
// default: false
|
||||
ParseEnv bool
|
||||
// ValueHook before set value hook TODO
|
||||
ValueHook func(val string) any
|
||||
}
|
||||
|
||||
// InitDefaults init struct default value by field "default" tag.
|
||||
//
|
||||
// TIPS:
|
||||
//
|
||||
// Only support init set: string, bool, intX, uintX, floatX
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// type User1 struct {
|
||||
// Name string `default:"inhere"`
|
||||
// Age int32 `default:"30"`
|
||||
// }
|
||||
//
|
||||
// u1 := &User1{}
|
||||
// err = structs.InitDefaults(u1)
|
||||
// fmt.Printf("%+v\n", u1)
|
||||
// // Output: {Name:inhere Age:30}
|
||||
func InitDefaults(ptr any, optFns ...InitOptFunc) error {
|
||||
// NewWriter create a struct writer
|
||||
func NewWriter(ptr any) *Wrapper {
|
||||
rv := reflect.ValueOf(ptr)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
return errors.New("must be provider an pointer value")
|
||||
if rv.Kind() != reflect.Pointer {
|
||||
panic("must be provider an pointer value")
|
||||
}
|
||||
|
||||
rv = rv.Elem()
|
||||
if rv.Kind() != reflect.Struct {
|
||||
return errors.New("must be provider an struct value")
|
||||
}
|
||||
|
||||
opt := &InitOptions{TagName: defaultInitTag}
|
||||
for _, fn := range optFns {
|
||||
fn(opt)
|
||||
}
|
||||
|
||||
return initDefaults(rv, opt)
|
||||
}
|
||||
|
||||
func initDefaults(rv reflect.Value, opt *InitOptions) error {
|
||||
rt := rv.Type()
|
||||
|
||||
for i := 0; i < rt.NumField(); i++ {
|
||||
ft := rt.Field(i)
|
||||
// skip don't exported field
|
||||
if ft.Name[0] >= 'a' && ft.Name[0] <= 'z' {
|
||||
continue
|
||||
}
|
||||
|
||||
fv := rv.Field(i)
|
||||
if fv.Kind() == reflect.Struct {
|
||||
if err := initDefaults(fv, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// skip on field has value
|
||||
if !fv.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
tagVal := ft.Tag.Get(opt.TagName)
|
||||
if err := initDefaultValue(fv, tagVal, opt.ParseEnv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func initDefaultValue(fv reflect.Value, val string, parseEnv bool) error {
|
||||
if val == "" || !fv.CanSet() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse env var
|
||||
if parseEnv {
|
||||
val = comfunc.ParseEnvVar(val, nil)
|
||||
}
|
||||
|
||||
// set value
|
||||
return reflects.SetValue(fv, val)
|
||||
return WrapValue(rv)
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* load values to a struct
|
||||
* set values to a struct
|
||||
*************************************************************/
|
||||
|
||||
// SetOptFunc define
|
||||
@@ -127,15 +38,22 @@ type SetOptions struct {
|
||||
//
|
||||
// see InitDefaults()
|
||||
ParseDefault bool
|
||||
|
||||
// DefaultValTag name. tag: default
|
||||
DefaultValTag string
|
||||
|
||||
// ParseDefaultEnv parse env var on default tag. eg: `default:"${APP_ENV}"`
|
||||
//
|
||||
// default: false
|
||||
ParseDefaultEnv bool
|
||||
}
|
||||
|
||||
// SetValues set data values to struct ptr
|
||||
// WithParseDefault value by tag "default"
|
||||
func WithParseDefault(opt *SetOptions) {
|
||||
opt.ParseDefault = true
|
||||
}
|
||||
|
||||
// SetValues set values to struct ptr from map data.
|
||||
//
|
||||
// TIPS:
|
||||
//
|
||||
@@ -152,7 +70,7 @@ func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error {
|
||||
}
|
||||
|
||||
opt := &SetOptions{
|
||||
FieldTagName: "json",
|
||||
FieldTagName: defaultFieldTag,
|
||||
DefaultValTag: defaultInitTag,
|
||||
}
|
||||
|
||||
@@ -163,7 +81,7 @@ func SetValues(ptr any, data map[string]any, optFns ...SetOptFunc) error {
|
||||
}
|
||||
|
||||
func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
|
||||
if data == nil || len(data) == 0 {
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -184,7 +102,6 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
name = info.Get("name")
|
||||
}
|
||||
|
||||
@@ -192,20 +109,27 @@ func setValues(rv reflect.Value, data map[string]any, opt *SetOptions) error {
|
||||
val, ok := data[name]
|
||||
|
||||
// set field value by default tag.
|
||||
if !ok && fv.IsZero() {
|
||||
if !ok && opt.ParseDefault && fv.IsZero() {
|
||||
defVal := ft.Tag.Get(opt.DefaultValTag)
|
||||
|
||||
if err := initDefaultValue(fv, defVal, opt.ParseDefaultEnv); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// handle for pointer field
|
||||
if fv.Kind() == reflect.Pointer {
|
||||
if fv.IsNil() {
|
||||
fv.Set(reflect.New(fv.Type().Elem()))
|
||||
}
|
||||
fv = fv.Elem()
|
||||
}
|
||||
|
||||
// field is struct
|
||||
if fv.Kind() == reflect.Struct {
|
||||
asMp, ok := val.(map[string]any)
|
||||
if !ok {
|
||||
return fmt.Errorf("field is struct, must provide map data value")
|
||||
asMp, err := maputil.TryAnyMap(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("must provide map data for field %q, err=%v", ft.Name, err)
|
||||
}
|
||||
|
||||
if err := setValues(fv, asMp, opt); err != nil {
|
||||
Reference in New Issue
Block a user