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:
dependabot[bot]
2023-06-13 10:54:58 +02:00
committed by Ralf Haferkamp
parent 03045c5ccc
commit 5ebc596352
190 changed files with 18970 additions and 4167 deletions
+89 -21
View File
@@ -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
View File
@@ -0,0 +1,6 @@
package structs
// MapStruct simple copy src struct value to dst struct
// func MapStruct(srcSt, dstSt any) {
// // TODO
// }
+19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
}
@@ -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 {