Merge pull request #241 from colincross/tagindex
Add PropertyIndexesWithTag
This commit is contained in:
commit
a9df651359
4 changed files with 209 additions and 13 deletions
|
@ -80,12 +80,14 @@ bootstrap_go_package {
|
|||
"proptools/escape.go",
|
||||
"proptools/extend.go",
|
||||
"proptools/proptools.go",
|
||||
"proptools/tag.go",
|
||||
"proptools/typeequal.go",
|
||||
],
|
||||
testSrcs: [
|
||||
"proptools/clone_test.go",
|
||||
"proptools/escape_test.go",
|
||||
"proptools/extend_test.go",
|
||||
"proptools/tag_test.go",
|
||||
"proptools/typeequal_test.go",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
package proptools
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
@ -39,17 +37,6 @@ func FieldNameForProperty(propertyName string) string {
|
|||
return fieldName
|
||||
}
|
||||
|
||||
func HasTag(field reflect.StructField, name, value string) bool {
|
||||
tag := field.Tag.Get(name)
|
||||
for _, entry := range strings.Split(tag, ",") {
|
||||
if entry == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// BoolPtr returns a pointer to a new bool containing the given value.
|
||||
func BoolPtr(b bool) *bool {
|
||||
return &b
|
||||
|
|
67
proptools/tag.go
Normal file
67
proptools/tag.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2019 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proptools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// HasTag returns true if a StructField has a tag in the form `name:"foo,value"`.
|
||||
func HasTag(field reflect.StructField, name, value string) bool {
|
||||
tag := field.Tag.Get(name)
|
||||
for _, entry := range strings.Split(tag, ",") {
|
||||
if entry == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// PropertyIndexesWithTag returns the indexes of all properties (in the form used by reflect.Value.FieldByIndex) that
|
||||
// are tagged with the given key and value, including ones found in embedded structs or pointers to structs.
|
||||
func PropertyIndexesWithTag(ps interface{}, key, value string) [][]int {
|
||||
t := reflect.TypeOf(ps)
|
||||
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
|
||||
panic(fmt.Errorf("type %s is not a pointer to a struct", t))
|
||||
}
|
||||
t = t.Elem()
|
||||
|
||||
return propertyIndexesWithTag(t, key, value)
|
||||
}
|
||||
func propertyIndexesWithTag(t reflect.Type, key, value string) [][]int {
|
||||
var indexes [][]int
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
ft := field.Type
|
||||
if ft.Kind() == reflect.Struct || (ft.Kind() == reflect.Ptr && ft.Elem().Kind() == reflect.Struct) {
|
||||
if ft.Kind() == reflect.Ptr {
|
||||
ft = ft.Elem()
|
||||
}
|
||||
subIndexes := propertyIndexesWithTag(ft, key, value)
|
||||
for _, sub := range subIndexes {
|
||||
sub = append([]int{i}, sub...)
|
||||
indexes = append(indexes, sub)
|
||||
}
|
||||
} else if HasTag(field, key, value) {
|
||||
indexes = append(indexes, field.Index)
|
||||
}
|
||||
}
|
||||
|
||||
return indexes
|
||||
}
|
140
proptools/tag_test.go
Normal file
140
proptools/tag_test.go
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2019 Google Inc. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package proptools
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHasTag(t *testing.T) {
|
||||
type testType struct {
|
||||
NoTag string
|
||||
EmptyTag string ``
|
||||
OtherTag string `foo:"bar"`
|
||||
MatchingTag string `name:"value"`
|
||||
ExtraValues string `name:"foo,value,bar"`
|
||||
ExtraTags string `foo:"bar" name:"value"`
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
field string
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
field: "NoTag",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
field: "EmptyTag",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
field: "OtherTag",
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
field: "MatchingTag",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
field: "ExtraValues",
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
field: "ExtraTags",
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.field, func(t *testing.T) {
|
||||
field, _ := reflect.TypeOf(testType{}).FieldByName(test.field)
|
||||
if got := HasTag(field, "name", "value"); got != test.want {
|
||||
t.Errorf(`HasTag(%q, "name", "value") = %v, want %v`, field.Tag, got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPropertyIndexesWithTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
ps interface{}
|
||||
want [][]int
|
||||
}{
|
||||
{
|
||||
name: "none",
|
||||
ps: &struct {
|
||||
Foo string
|
||||
}{},
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "one",
|
||||
ps: &struct {
|
||||
Foo string `name:"value"`
|
||||
}{},
|
||||
want: [][]int{{0}},
|
||||
},
|
||||
{
|
||||
name: "two",
|
||||
ps: &struct {
|
||||
Foo string `name:"value"`
|
||||
Bar string `name:"value"`
|
||||
}{},
|
||||
want: [][]int{{0}, {1}},
|
||||
},
|
||||
{
|
||||
name: "some",
|
||||
ps: &struct {
|
||||
Foo string `name:"other"`
|
||||
Bar string `name:"value"`
|
||||
}{},
|
||||
want: [][]int{{1}},
|
||||
},
|
||||
{
|
||||
name: "embedded",
|
||||
ps: &struct {
|
||||
Foo struct {
|
||||
Bar string `name:"value"`
|
||||
}
|
||||
}{},
|
||||
want: [][]int{{0, 0}},
|
||||
},
|
||||
{
|
||||
name: "embedded ptr",
|
||||
ps: &struct {
|
||||
Foo *struct {
|
||||
Bar string `name:"value"`
|
||||
}
|
||||
}{},
|
||||
want: [][]int{{0, 0}},
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
ps: (*struct {
|
||||
Foo string `name:"value"`
|
||||
})(nil),
|
||||
want: [][]int{{0}},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if got := PropertyIndexesWithTag(test.ps, "name", "value"); !reflect.DeepEqual(got, test.want) {
|
||||
t.Errorf("PropertyIndexesWithTag() = %v, want %v", got, test.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue