From 23e6715a02d35ff68d036f8894750be2d2ce4996 Mon Sep 17 00:00:00 2001 From: Ellison Marks Date: Mon, 26 Oct 2015 16:00:56 -0700 Subject: [PATCH] Making the field name matching when merging respect the toml struct tag. If the field has a toml struct tag, don't try fuzzy matching, thanks to @ekini. --- config.go | 33 +++++++++++++++++++++++++-------- config_test.go | 28 ++++++---------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/config.go b/config.go index e6b653015..8ca8a2c0f 100644 --- a/config.go +++ b/config.go @@ -369,12 +369,29 @@ func PrintOutputConfig(name string) error { return nil } -// Used for fuzzy matching struct field names in FieldByNameFunc calls below -func fieldMatch(field string) func(string) bool { - return func(name string) bool { - r := strings.NewReplacer("_", "") - return strings.ToLower(name) == strings.ToLower(r.Replace(field)) +// Find the field with a name matching fieldName, respecting the struct tag and ignoring case and underscores. +// If no field is found, return the zero reflect.Value, which should be checked for with .IsValid(). +func findField(fieldName string, value reflect.Value) reflect.Value { + r := strings.NewReplacer("_", "") + vType := value.Type() + for i := 0; i < vType.NumField(); i++ { + fieldType := vType.Field(i) + + // if we have toml tag, use it + if tag := fieldType.Tag.Get("toml"); tag != "" { + if tag == "-" { // omit + continue + } + if tag == fieldName { + return value.Field(i) + } + } else { + if strings.ToLower(fieldType.Name) == strings.ToLower(r.Replace(fieldName)) { + return value.Field(i) + } + } } + return reflect.Value{} } // A very limited merge. Merges the fields named in the fields parameter, replacing most values, but appending to arrays. @@ -388,15 +405,15 @@ func mergeStruct(base, overlay interface{}, fields []string) error { return fmt.Errorf("Tried to merge two different types: %v and %v", baseValue.Type(), overlayValue.Type()) } for _, field := range fields { - overlayFieldValue := overlayValue.FieldByNameFunc(fieldMatch(field)) + overlayFieldValue := findField(field, overlayValue) if !overlayFieldValue.IsValid() { return fmt.Errorf("could not find field in %v matching %v", overlayValue.Type(), field) } + baseFieldValue := findField(field, baseValue) if overlayFieldValue.Kind() == reflect.Slice { - baseFieldValue := baseValue.FieldByNameFunc(fieldMatch(field)) baseFieldValue.Set(reflect.AppendSlice(baseFieldValue, overlayFieldValue)) } else { - baseValue.FieldByNameFunc(fieldMatch(field)).Set(overlayFieldValue) + baseFieldValue.Set(overlayFieldValue) } } return nil diff --git a/config_test.go b/config_test.go index 0e3c553e4..e5414ac0c 100644 --- a/config_test.go +++ b/config_test.go @@ -16,22 +16,6 @@ import ( "github.com/stretchr/testify/suite" ) -func TestConfig_fieldMatch(t *testing.T) { - assert := assert.New(t) - - matchFunc := fieldMatch("testfield") - assert.True(matchFunc("testField"), "testfield should match testField") - assert.True(matchFunc("TestField"), "testfield should match TestField") - assert.True(matchFunc("TESTFIELD"), "testfield should match TESTFIELD") - assert.False(matchFunc("OtherField"), "testfield should not match OtherField") - - matchFunc = fieldMatch("test_field") - assert.True(matchFunc("testField"), "test_field should match testField") - assert.True(matchFunc("TestField"), "test_field should match TestField") - assert.True(matchFunc("TESTFIELD"), "test_field should match TESTFIELD") - assert.False(matchFunc("OtherField"), "test_field should not match OtherField") -} - type subTest struct { AField string AnotherField int @@ -40,7 +24,7 @@ type test struct { StringField string IntegerField int FloatField float32 - BooleanField bool + BooleansField bool `toml:"boolean_field"` DatetimeField time.Time ArrayField []string TableArrayField []subTest @@ -67,7 +51,7 @@ func (s *MergeStructSuite) SetupTest() { StringField: "one", IntegerField: 1, FloatField: 1.1, - BooleanField: false, + BooleansField: false, DatetimeField: time.Date(1963, time.August, 28, 17, 0, 0, 0, time.UTC), ArrayField: []string{"one", "two", "three"}, TableArrayField: []subTest{ @@ -85,7 +69,7 @@ func (s *MergeStructSuite) SetupTest() { StringField: "two", IntegerField: 2, FloatField: 2.2, - BooleanField: true, + BooleansField: true, DatetimeField: time.Date(1965, time.March, 25, 17, 0, 0, 0, time.UTC), ArrayField: []string{"four", "five", "six"}, TableArrayField: []subTest{ @@ -114,7 +98,7 @@ func (s *MergeStructSuite) TestFullMerge() { StringField: "two", IntegerField: 2, FloatField: 2.2, - BooleanField: true, + BooleansField: true, DatetimeField: time.Date(1965, time.March, 25, 17, 0, 0, 0, time.UTC), ArrayField: []string{"one", "two", "three", "four", "five", "six"}, TableArrayField: []subTest{ @@ -149,7 +133,7 @@ func (s *MergeStructSuite) TestPartialMergeWithoutSlices() { StringField: "two", IntegerField: 1, FloatField: 2.2, - BooleanField: false, + BooleansField: false, DatetimeField: time.Date(1965, time.March, 25, 17, 0, 0, 0, time.UTC), ArrayField: []string{"one", "two", "three"}, TableArrayField: []subTest{ @@ -176,7 +160,7 @@ func (s *MergeStructSuite) TestPartialMergeWithSlices() { StringField: "two", IntegerField: 1, FloatField: 2.2, - BooleanField: false, + BooleansField: false, DatetimeField: time.Date(1965, time.March, 25, 17, 0, 0, 0, time.UTC), ArrayField: []string{"one", "two", "three"}, TableArrayField: []subTest{