struct: reflect slice elements to shadows when 'allowShadow' tag is presented (#196)

This commit is contained in:
Unknwon
2019-07-06 22:25:57 -07:00
parent 2d4014bce1
commit 8659100d2d
3 changed files with 73 additions and 10 deletions
+1 -1
View File
@@ -36,7 +36,7 @@ const (
// Maximum allowed depth when recursively substituing variable names.
depthValues = 99
version = "1.43.1"
version = "1.44.0"
)
// Version returns current package version literal.
+38 -9
View File
@@ -349,14 +349,43 @@ func StrictMapTo(v, source interface{}, others ...interface{}) error {
}
// reflectSliceWithProperType does the opposite thing as setSliceWithProperType.
func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error {
func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error {
slice := field.Slice(0, field.Len())
if field.Len() == 0 {
return nil
}
sliceOf := field.Type().Elem().Kind()
if allowShadow {
var keyWithShadows *Key
for i := 0; i < field.Len(); i++ {
var val string
switch sliceOf {
case reflect.String:
val = slice.Index(i).String()
case reflect.Int, reflect.Int64:
val = fmt.Sprint(slice.Index(i).Int())
case reflect.Uint, reflect.Uint64:
val = fmt.Sprint(slice.Index(i).Uint())
case reflect.Float64:
val = fmt.Sprint(slice.Index(i).Float())
case reflectTime:
val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339)
default:
return fmt.Errorf("unsupported type '[]%s'", sliceOf)
}
if i == 0 {
keyWithShadows = newKey(key.s, key.name, val)
} else {
keyWithShadows.AddShadow(val)
}
}
key = keyWithShadows
return nil
}
var buf bytes.Buffer
sliceOf := field.Type().Elem().Kind()
for i := 0; i < field.Len(); i++ {
switch sliceOf {
case reflect.String:
@@ -374,12 +403,12 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) err
}
buf.WriteString(delim)
}
key.SetValue(buf.String()[:buf.Len()-1])
key.SetValue(buf.String()[:buf.Len()-len(delim)])
return nil
}
// reflectWithProperType does the opposite thing as setWithProperType.
func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error {
func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error {
switch t.Kind() {
case reflect.String:
key.SetValue(field.String())
@@ -394,7 +423,7 @@ func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim
case reflectTime:
key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339)))
case reflect.Slice:
return reflectSliceWithProperType(key, field, delim)
return reflectSliceWithProperType(key, field, delim, allowShadow)
default:
return fmt.Errorf("unsupported type '%s'", t)
}
@@ -439,12 +468,12 @@ func (s *Section) reflectFrom(val reflect.Value) error {
continue
}
opts := strings.SplitN(tag, ",", 2)
if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) {
rawName, omitEmpty, allowShadow := parseTagOptions(tag)
if omitEmpty && isEmptyValue(field) {
continue
}
fieldName := s.parseFieldName(tpField.Name, opts[0])
fieldName := s.parseFieldName(tpField.Name, rawName)
if len(fieldName) == 0 || !field.CanSet() {
continue
}
@@ -480,7 +509,7 @@ func (s *Section) reflectFrom(val reflect.Value) error {
key.Comment = tpField.Tag.Get("comment")
}
if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil {
if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim")), allowShadow); err != nil {
return fmt.Errorf("error reflecting field (%s): %v", fieldName, err)
}
+34
View File
@@ -375,6 +375,40 @@ omitempty = 9
})
}
// Inspired by https://github.com/go-ini/ini/issues/196
func TestMapToAndReflectFromStructWithShadows(t *testing.T) {
Convey("Map to struct and then reflect with shadows should generate original config content", t, func() {
type include struct {
Paths []string `ini:"path,omitempty,allowshadow"`
}
cfg, err := ini.LoadSources(ini.LoadOptions{
AllowShadows: true,
}, []byte(`
[include]
path = /tmp/gpm-profiles/test5.profile
path = /tmp/gpm-profiles/test1.profile`))
So(err, ShouldBeNil)
sec := cfg.Section("include")
inc := new(include)
err = sec.MapTo(inc)
So(err, ShouldBeNil)
err = sec.ReflectFrom(inc)
So(err, ShouldBeNil)
var buf bytes.Buffer
_, err = cfg.WriteTo(&buf)
So(err, ShouldBeNil)
So(buf.String(), ShouldEqual, `[include]
path = /tmp/gpm-profiles/test5.profile
path = /tmp/gpm-profiles/test1.profile
`)
})
}
type testMapper struct {
PackageName string
}