Skip to content

Commit

Permalink
feat: #94 - Support parse default value by struct tag default
Browse files Browse the repository at this point in the history
  • Loading branch information
inhere committed Aug 28, 2022
1 parent b66f432 commit edd2db6
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 4 deletions.
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Golang's application config manage tool library.
- allow events: `set.value`, `set.data`, `load.data`, `clean.data`
- Support data overlay and merge, automatically load by key when loading multiple copies of data
- Support for binding all or part of the configuration data to the structure
- Support init default value by struct tag `default`
- Support get sub value by path, like `map.key` `arr.2`
- Support parse ENV name and allow with default value. like `envKey: ${SHELL|/bin/bash}` -> `envKey: /bin/zsh`
- Generic api `Get` `Int` `Uint` `Int64` `Float` `String` `Bool` `Ints` `IntMap` `Strings` `StringMap` ...
Expand Down Expand Up @@ -329,9 +330,47 @@ type Options struct {
DecoderConfig *mapstructure.DecoderConfig
// HookFunc on data changed.
HookFunc HookFunc
// ParseDefault tag on binding data to struct. tag: default
ParseDefault bool
}
```

### Options: Parse default

Support parse default value by struct tag `default`

```go
// add option: config.ParseDefault
c := config.New("test").WithOptions(config.ParseDefault)

// only set name
c.SetData(map[string]interface{}{
"name": "inhere",
})

// age load from default tag
type User struct {
Age int `default:"30"`
Name string
Tags []int
}

user := &User{}
goutil.MustOk(c.Decode(user))
dump.Println(user)
```

**Output**:

```shell
&config_test.User {
Age: int(30),
Name: string("inhere"), #len=6
Tags: []int [ #len=0
],
},
```

## API Methods Refer

### Load Config
Expand Down
39 changes: 39 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
- 可用事件: `set.value`, `set.data`, `load.data`, `clean.data`
- 支持数据覆盖合并,加载多份数据时将按key自动合并
- 支持将全部或部分配置数据绑定到结构体 `config.BindStruct("key", &s)`
- NEW: 支持通过结构标签 `default` 解析并设置默认值
- 支持通过 `.` 分隔符来按路径获取子级值,也支持自定义分隔符。 e.g `map.key` `arr.2`
- 支持解析ENV变量名称。 like `shell: ${SHELL}` -> `shell: /bin/zsh`
- 简洁的使用API `Get` `Int` `Uint` `Int64` `String` `Bool` `Ints` `IntMap` `Strings` `StringMap` ...
Expand Down Expand Up @@ -309,9 +310,47 @@ type Options struct {
DecoderConfig *mapstructure.DecoderConfig
// HookFunc on data changed.
HookFunc HookFunc
// ParseDefault tag on binding data to struct. tag: default
ParseDefault bool
}
```

### 选项: 解析默认值

NEW: 支持通过结构标签 `default` 解析并设置默认值

```go
// add option: config.ParseDefault
c := config.New("test").WithOptions(config.ParseDefault)

// only set name
c.SetData(map[string]interface{}{
"name": "inhere",
})

// age load from default tag
type User struct {
Age int `default:"30"`
Name string
Tags []int
}

user := &User{}
goutil.MustOk(c.Decode(user))
dump.Println(user)
```

**Output**:

```shell
&config_test.User {
Age: int(30),
Name: string("inhere"), #len=6
Tags: []int [ #len=0
],
},
```

## API方法参考

### 载入配置
Expand Down
7 changes: 6 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ const (
JSON = "json"
Yaml = "yaml"
Toml = "toml"
Prop = "properties"
)

const (
// default delimiter
defaultDelimiter byte = '.'
// default struct tag name for binding data to struct
defaultStructTag = "mapstructure"
// struct tag name for set default-value on binding data
defaultValueTag = "default"
)

// internal vars
Expand All @@ -70,7 +75,7 @@ var dc = New("default")

// Config structure definition
type Config struct {
// save latest error, will clear after read.
// save the latest error, will clear after read.
err error
// config instance name
name string
Expand Down
9 changes: 9 additions & 0 deletions export.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"os"

"github.com/gookit/goutil/structs"
"github.com/mitchellh/mapstructure"
)

Expand Down Expand Up @@ -83,6 +84,14 @@ func (c *Config) Structure(key string, dst interface{}) error {
}
}

// init default value by tag: default
if c.opts.ParseDefault {
err := structs.InitDefaults(dst)
if err != nil {
return err
}
}

bindConf := c.opts.makeDecoderConfig()
// set result struct ptr
bindConf.Result = dst
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.16

require (
github.com/BurntSushi/toml v1.2.0
github.com/gookit/goutil v0.5.9
github.com/gookit/goutil v0.5.10
github.com/gookit/ini/v2 v2.1.1
github.com/gookit/properties v0.1.0
github.com/hashicorp/hcl v1.0.0
Expand Down
3 changes: 2 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl
github.com/gookit/color v1.5.1 h1:Vjg2VEcdHpwq+oY63s/ksHrgJYCTo0bwWvmmYWdE9fQ=
github.com/gookit/color v1.5.1/go.mod h1:wZFzea4X8qN6vHOSP2apMb4/+w/orMznEzYsIHPaqKM=
github.com/gookit/goutil v0.5.3/go.mod h1:W0rVeVN9EcoRV+ODq91TXvqoUA87BXGi36WFVykCKRI=
github.com/gookit/goutil v0.5.9 h1:DBaSHaealbqxLWEZrEt/vqnPKiJHLQJau9dbcFQG/ng=
github.com/gookit/goutil v0.5.9/go.mod h1:iZLXpRhMqKGvKtJ9+b0cdls2gXRH4HaGWQfkf2mdHRQ=
github.com/gookit/goutil v0.5.10 h1:tKvo3LzE6j8vTXizQoPvZq9jt4VrVriuD5lQMbQbhH4=
github.com/gookit/goutil v0.5.10/go.mod h1:rZCjNKf63zWuD5N0vyn6718utvN9m2uT0jsHBV66hAU=
github.com/gookit/ini/v2 v2.1.1 h1:q2VtSSl/ivTOZMPvxhjWxO3f146NvWM84jBQZETj/1o=
github.com/gookit/ini/v2 v2.1.1/go.mod h1:zkMTCrnE2QgDW0izB/pRtgEKKjXmT/22dfk5eZg+IHo=
github.com/gookit/properties v0.1.0 h1:UHwgz0UCjxJ0q36qmvOAWNSdNh+yGIBUjUDaKzLryJY=
Expand Down
38 changes: 38 additions & 0 deletions issues_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,44 @@ func TestIssues_81(t *testing.T) {
is.Eq(wantTm, opt.IdleTime)
}

// https://github.com/gookit/config/issues/94
func TestIssues_94(t *testing.T) {
is := assert.New(t)
// add option: config.ParseDefault
c := config.New("test").WithOptions(config.ParseDefault)

// only set name
c.SetData(map[string]interface{}{
"name": "inhere",
})

// age load from default tag
type User struct {
Age int `json:"age" default:"30"`
Name string
Tags []int
}

user := &User{}
is.NoErr(c.Decode(user))
dump.Println(user)
is.Eq("inhere", user.Name)
is.Eq(30, user.Age)

// field use ptr
type User1 struct {
Age *int `json:"age" default:"30"`
Name string
Tags []int
}

u1 := &User1{}
is.NoErr(c.Decode(u1))
dump.Println(u1)
is.Eq("inhere", u1.Name)
is.Eq(30, *u1.Age)
}

// https://github.com/gookit/config/issues/96
func TestIssues_96(t *testing.T) {
is := assert.New(t)
Expand Down
3 changes: 2 additions & 1 deletion util.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import (
"github.com/mitchellh/mapstructure"
)

// ValDecodeHookFunc returns a mapstructure.DecodeHookFunc that parse ENV var, and more custom parse
// ValDecodeHookFunc returns a mapstructure.DecodeHookFunc
// that parse ENV var, and more custom parse
func ValDecodeHookFunc(parseEnv, parseTime bool) mapstructure.DecodeHookFunc {
return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) {
if f.Kind() != reflect.String {
Expand Down

0 comments on commit edd2db6

Please sign in to comment.