diff --git a/structure.go b/structure.go index c4174fc..589f7e2 100644 --- a/structure.go +++ b/structure.go @@ -88,6 +88,37 @@ func Values(s interface{}) []interface{} { } +// Fields returns a slice of field names. A struct tag with the content of "-" +// ignores the checking of that particular field. Example: +// +// // Field is ignored by this package. +// Field bool `structure:"-"` +// +// Note that only exported fields of a struct can be accessed, non exported +// fields will be neglected. It panics if s's kind is not struct. +func Fields(s interface{}) []string { + v, fields := strctInfo(s) + + keys := make([]string, 0) + for _, field := range fields { + val := v.FieldByName(field.Name) + + _, tagOpts := parseTag(field.Tag.Get(DefaultTagName)) + + if IsStruct(val.Interface()) && !tagOpts.Has("omitnested") { + // look out for embedded structs, and convert them to a + // []string to be added to the final values slice + for _, embeddedVal := range Fields(val.Interface()) { + keys = append(keys, embeddedVal) + } + } + + keys = append(keys, field.Name) + } + + return keys +} + // IsZero returns true if all fields in a struct is a zero value (not // initialized) A struct tag with the content of "-" ignores the checking of // that particular field. Example: @@ -163,34 +194,6 @@ func HasZero(s interface{}) bool { return false } -// Fields returns a slice of field names. A struct tag with the content of "-" -// ignores the checking of that particular field. Example: -// -// // Field is ignored by this package. -// Field bool `structure:"-"` -// -// Note that only exported fields of a struct can be accessed, non exported -// fields will be neglected. It panics if s's kind is not struct. -func Fields(s interface{}) []string { - v, fields := strctInfo(s) - - keys := make([]string, 0) - for _, field := range fields { - val := v.FieldByName(field.Name) - if IsStruct(val.Interface()) { - // look out for embedded structs, and convert them to a - // []string to be added to the final values slice - for _, embeddedVal := range Fields(val.Interface()) { - keys = append(keys, embeddedVal) - } - } - - keys = append(keys, field.Name) - } - - return keys -} - // IsStruct returns true if the given variable is a struct or a pointer to // struct. func IsStruct(s interface{}) bool { diff --git a/structure_test.go b/structure_test.go index 1ab8e7e..5d14a3a 100644 --- a/structure_test.go +++ b/structure_test.go @@ -411,6 +411,43 @@ func TestFields(t *testing.T) { } } +func TestFields_OmitNested(t *testing.T) { + type A struct { + Name string + Value string + Number int + Enabled bool + } + a := A{Name: "example"} + + type B struct { + A A `structure:",omitnested"` + C int + } + b := &B{A: a, C: 123} + + s := Fields(b) + + if len(s) != 2 { + t.Errorf("Fields should omit nested struct. Expecting 2 got: %d", len(s)) + } + + inSlice := func(val interface{}) bool { + for _, v := range s { + if reflect.DeepEqual(v, val) { + return true + } + } + return false + } + + for _, val := range []interface{}{"A", "C"} { + if !inSlice(val) { + t.Errorf("Fields should have the value %v", val) + } + } +} + func TestFields_Nested(t *testing.T) { type A struct { Name string