mirror of
https://github.com/go-ini/ini.git
synced 2026-06-19 21:46:45 +02:00
struct: reflect slice elements to shadows when 'allowShadow' tag is presented (#196)
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user