Add PropertyIndexesWithTag
Add a function that returns all of the indexes to properties in a property struct that are tagged with `name:"value"`. Test: proptools/tag_test.go Change-Id: I00294934c1a0383c8b64ecaabc0e138682efb2e5
This commit is contained in:
parent
17a0b96440
commit
a4f6a3bebf
4 changed files with 209 additions and 13 deletions
|
@ -80,12 +80,14 @@ bootstrap_go_package {
|
||||||
"proptools/escape.go",
|
"proptools/escape.go",
|
||||||
"proptools/extend.go",
|
"proptools/extend.go",
|
||||||
"proptools/proptools.go",
|
"proptools/proptools.go",
|
||||||
|
"proptools/tag.go",
|
||||||
"proptools/typeequal.go",
|
"proptools/typeequal.go",
|
||||||
],
|
],
|
||||||
testSrcs: [
|
testSrcs: [
|
||||||
"proptools/clone_test.go",
|
"proptools/clone_test.go",
|
||||||
"proptools/escape_test.go",
|
"proptools/escape_test.go",
|
||||||
"proptools/extend_test.go",
|
"proptools/extend_test.go",
|
||||||
|
"proptools/tag_test.go",
|
||||||
"proptools/typeequal_test.go",
|
"proptools/typeequal_test.go",
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
package proptools
|
package proptools
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
)
|
)
|
||||||
|
@ -39,17 +37,6 @@ func FieldNameForProperty(propertyName string) string {
|
||||||
return fieldName
|
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.
|
// BoolPtr returns a pointer to a new bool containing the given value.
|
||||||
func BoolPtr(b bool) *bool {
|
func BoolPtr(b bool) *bool {
|
||||||
return &b
|
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