diff --git a/cache.go b/cache.go index c0c3884..a594291 100644 --- a/cache.go +++ b/cache.go @@ -17,21 +17,19 @@ var invalidPath = errors.New("schema: invalid path") // newCache returns a new cache. func newCache() *cache { c := cache{ - m: make(map[reflect.Type]*structInfo), - regconv: make(map[reflect.Type]Converter), - tag: "schema", - defaultTag: "default", + m: make(map[reflect.Type]*structInfo), + regconv: make(map[reflect.Type]Converter), + tag: "schema", } return &c } // cache caches meta-data about a struct. type cache struct { - l sync.RWMutex - m map[reflect.Type]*structInfo - regconv map[reflect.Type]Converter - tag string - defaultTag string + l sync.RWMutex + m map[reflect.Type]*structInfo + regconv map[reflect.Type]Converter + tag string } // registerConverter registers a converter function for a custom type. @@ -199,7 +197,7 @@ func (c *cache) createField(field reflect.StructField, parentAlias string) *fiel isSliceOfStructs: isSlice && isStruct, isAnonymous: field.Anonymous, isRequired: options.Contains("required"), - defaultValue: field.Tag.Get(c.defaultTag), + defaultValue: options.getDefaultOptionValue(), } } @@ -307,3 +305,16 @@ func (o tagOptions) Contains(option string) bool { } return false } + +func (o tagOptions) getDefaultOptionValue() string { + for _, s := range o { + if strings.HasPrefix(s, "default:") { + if t := strings.Split(s, ":"); len(t) > 0 { + return t[1] + } + break + } + } + + return "" +} diff --git a/decoder.go b/decoder.go index 161ec4a..8fe62fe 100644 --- a/decoder.go +++ b/decoder.go @@ -117,12 +117,12 @@ func (d *Decoder) setDefaults(t reflect.Type, v reflect.Value) MultiError { errs.merge(MultiError{"default-" + f.name: errors.New("required fields cannot have a default value")}) } else if f.defaultValue != "" && vCurrent.IsZero() && !f.isRequired { if f.typ.Kind() == reflect.Struct || f.typ.Kind() == reflect.Slice { - errs.merge(MultiError{"default-" + f.name: errors.New("default tag is supported only on: bool, float variants, string, unit variants types or their corresponding pointers")}) + errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers")}) } else if f.typ.Kind() == reflect.Ptr { t1 := f.typ.Elem() if t1.Kind() == reflect.Struct || t1.Kind() == reflect.Slice { - errs.merge(MultiError{"default-" + f.name: errors.New("default tag is supported only on: bool, float variants, string, unit variants types or their corresponding pointers")}) + errs.merge(MultiError{"default-" + f.name: errors.New("default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers")}) } vCurrent.Set(convertPointer(t1.Kind(), f.defaultValue)) diff --git a/decoder_test.go b/decoder_test.go index 04176fa..3a9e3fa 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -2027,20 +2027,18 @@ func TestUnmashalPointerToEmbedded(t *testing.T) { func TestDefaultValuesAreSet(t *testing.T) { - //TODO: Test nesting and update repos - type N struct { - S1 string `schema:"s1" default:"test1"` - I2 int `schema:"i2" default:"22"` + S1 string `schema:"s1,default:test1"` + I2 int `schema:"i2,default:22"` } type D struct { N - S string `schema:"s" default:"test1"` - I int `schema:"i" default:"21"` - B bool `schema:"b" default:"false"` - F float64 `schema:"f" default:"3.14"` - U uint `schema:"u" default:"1"` + S string `schema:"s,default:test1"` + I int `schema:"i,default:21"` + B bool `schema:"b,default:false"` + F float64 `schema:"f,default:3.14"` + U uint `schema:"u,default:1"` } data := map[string][]string{} @@ -2071,11 +2069,11 @@ func TestDefaultValuesAreSet(t *testing.T) { type P struct { *N - S *string `schema:"s" default:"test1"` - I *int `schema:"i" default:"21"` - B *bool `schema:"b" default:"false"` - F *float64 `schema:"f" default:"3.14"` - U *uint `schema:"u" default:"1"` + S *string `schema:"s,default:test1"` + I *int `schema:"i,default:21"` + B *bool `schema:"b,default:false"` + F *float64 `schema:"f,default:3.14"` + U *uint `schema:"u,default:1"` } p := P{N: &N{}} @@ -2099,11 +2097,11 @@ func TestDefaultValuesAreSet(t *testing.T) { func TestDefaultValuesAreIgnoredIfValuesAreProvided(t *testing.T) { type D struct { - S string `schema:"s" default:"test1"` - I int `schema:"i" default:"21"` - B bool `schema:"b" default:"false"` - F float64 `schema:"f" default:"3.14"` - U uint `schema:"u" default:"1"` + S string `schema:"s,default:test1"` + I int `schema:"i,default:21"` + B bool `schema:"b,default:false"` + F float64 `schema:"f,default:3.14"` + U uint `schema:"u,default:1"` } data := map[string][]string{"s": {"s"}, "i": {"1"}, "b": {"true"}, "f": {"0.22"}, "u": {"14"}} @@ -2132,11 +2130,11 @@ func TestDefaultValuesAreIgnoredIfValuesAreProvided(t *testing.T) { func TestRequiredFieldsCannotHaveDefaults(t *testing.T) { type D struct { - S string `schema:"s,required" default:"test1"` - I int `schema:"i,required" default:"21"` - B bool `schema:"b,required" default:"false"` - F float64 `schema:"f,required" default:"3.14"` - U uint `schema:"u,required" default:"1"` + S string `schema:"s,required,default:test1"` + I int `schema:"i,required,default:21"` + B bool `schema:"b,required,default:false"` + F float64 `schema:"f,required,default:3.14"` + U uint `schema:"u,required,default:1"` } data := map[string][]string{"s": {"s"}, "i": {"1"}, "b": {"true"}, "f": {"0.22"}, "u": {"14"}} @@ -2158,8 +2156,8 @@ func TestRequiredFieldsCannotHaveDefaults(t *testing.T) { func TestDefaultsAreNotSupportedForStructsAndSlices(t *testing.T) { type D struct { - S S1 `schema:"s" default:"{f1:0}"` - A []string `schema:"s" default:"test1,test2"` + S S1 `schema:"s,default:{f1:0}"` + A []string `schema:"s,default:test1,test2"` } d := D{} @@ -2170,10 +2168,9 @@ func TestDefaultsAreNotSupportedForStructsAndSlices(t *testing.T) { err := decoder.Decode(&d, data) - expected := "default tag is supported only on: bool, float variants, string, unit variants types or their corresponding pointers" + expected := "default option is supported only on: bool, float variants, string, unit variants types or their corresponding pointers" if err == nil || !strings.Contains(err.Error(), expected) { t.Errorf("decoding should fail with error msg %s got %q", expected, err) } - }