From f3cf891d4f0cde03911ee438c5b879af8150cc61 Mon Sep 17 00:00:00 2001 From: MarkJoyMa Date: Thu, 2 Mar 2023 17:20:09 +0800 Subject: [PATCH] feat: conf add FillDefault func --- core/conf/config.go | 22 ++++++++++++++------ core/conf/config_test.go | 40 +++++++++++++++++++++++++++++++++++++ core/mapping/unmarshaler.go | 16 +++++++++++++-- 3 files changed, 70 insertions(+), 8 deletions(-) diff --git a/core/conf/config.go b/core/conf/config.go index ccc9e8ee..170c6c14 100644 --- a/core/conf/config.go +++ b/core/conf/config.go @@ -13,12 +13,17 @@ import ( "github.com/zeromicro/go-zero/internal/encoding" ) -var loaders = map[string]func([]byte, any) error{ - ".json": LoadFromJsonBytes, - ".toml": LoadFromTomlBytes, - ".yaml": LoadFromYamlBytes, - ".yml": LoadFromYamlBytes, -} +const jsonTagKey = "json" + +var ( + fillDefaultUnmarshaler = mapping.NewUnmarshaler(jsonTagKey, mapping.WithDefault()) + loaders = map[string]func([]byte, any) error{ + ".json": LoadFromJsonBytes, + ".toml": LoadFromTomlBytes, + ".yaml": LoadFromYamlBytes, + ".yml": LoadFromYamlBytes, + } +) // children and mapField should not be both filled. // named fields and map cannot be bound to the same field name. @@ -27,6 +32,11 @@ type fieldInfo struct { mapField *fieldInfo } +// FillDefault fills the default values for the given v. +func FillDefault(v any) error { + return fillDefaultUnmarshaler.Unmarshal(map[string]any{}, v) +} + // Load loads config into v from file, .json, .yaml and .yml are acceptable. func Load(file string, v any, opts ...Option) error { content, err := os.ReadFile(file) diff --git a/core/conf/config_test.go b/core/conf/config_test.go index 7ab3689f..33785db5 100644 --- a/core/conf/config_test.go +++ b/core/conf/config_test.go @@ -1039,3 +1039,43 @@ func createTempFile(ext, text string) (string, error) { return filename, nil } + +func TestFillDefaultUnmarshal(t *testing.T) { + t.Run("nil", func(t *testing.T) { + type St struct{} + err := FillDefault(St{}) + assert.Error(t, err) + }) + + t.Run("not nil", func(t *testing.T) { + type St struct{} + err := FillDefault(&St{}) + assert.NoError(t, err) + }) + + t.Run("default", func(t *testing.T) { + type St struct { + A string `json:",default=a"` + B string + } + var st St + err := FillDefault(&st) + assert.NoError(t, err) + assert.Equal(t, st.A, "a") + }) + + t.Run("env", func(t *testing.T) { + type St struct { + A string `json:",default=a"` + B string + C string `json:",env=TEST_C"` + } + t.Setenv("TEST_C", "c") + + var st St + err := FillDefault(&st) + assert.NoError(t, err) + assert.Equal(t, st.A, "a") + assert.Equal(t, st.C, "c") + }) +} diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index e4240cf2..5f3061d2 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -47,6 +47,7 @@ type ( UnmarshalOption func(*unmarshalOptions) unmarshalOptions struct { + fillDefault bool fromString bool canonicalKey func(key string) string } @@ -710,7 +711,7 @@ func (u *Unmarshaler) processNamedField(field reflect.StructField, value reflect valuer := createValuer(m, opts) mapValue, hasValue := getValue(valuer, canonicalKey) - if !hasValue { + if !hasValue || u.opts.fillDefault { return u.processNamedFieldWithoutValue(field.Type, value, opts, fullName) } @@ -801,6 +802,10 @@ func (u *Unmarshaler) processNamedFieldWithoutValue(fieldType reflect.Type, valu } } + if u.opts.fillDefault { + return nil + } + switch fieldKind { case reflect.Array, reflect.Map, reflect.Slice: if !opts.optional() { @@ -873,13 +878,20 @@ func WithStringValues() UnmarshalOption { } } -// WithCanonicalKeyFunc customizes an Unmarshaler with Canonical Key func +// WithCanonicalKeyFunc customizes an Unmarshaler with Canonical Key func. func WithCanonicalKeyFunc(f func(string) string) UnmarshalOption { return func(opt *unmarshalOptions) { opt.canonicalKey = f } } +// WithDefault customizes an Unmarshaler with fill default values. +func WithDefault() UnmarshalOption { + return func(opt *unmarshalOptions) { + opt.fillDefault = true + } +} + func createValuer(v valuerWithParent, opts *fieldOptionsWithContext) valuerWithParent { if opts.inherit() { return recursiveValuer{