Support AppendMatchingProperties on an embedded anonymous struct
Recurse into embedded anonymous structs and the BlueprintEmbed workaround structs when looking for properties in extendPropertiesRecursive. Test: proptools/extend_test.go Change-Id: I975651a64e5173747403629a09263562761f1495
This commit is contained in:
parent
1602226f23
commit
1c3530ab58
2 changed files with 198 additions and 1 deletions
|
@ -247,6 +247,8 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
||||||
prefix string, filter ExtendPropertyFilterFunc, sameTypes bool,
|
prefix string, filter ExtendPropertyFilterFunc, sameTypes bool,
|
||||||
orderFunc ExtendPropertyOrderFunc) error {
|
orderFunc ExtendPropertyOrderFunc) error {
|
||||||
|
|
||||||
|
dstValuesCopied := false
|
||||||
|
|
||||||
srcType := srcValue.Type()
|
srcType := srcValue.Type()
|
||||||
for i, srcField := range typeFields(srcType) {
|
for i, srcField := range typeFields(srcType) {
|
||||||
if srcField.PkgPath != "" {
|
if srcField.PkgPath != "" {
|
||||||
|
@ -284,7 +286,9 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
||||||
|
|
||||||
found := false
|
found := false
|
||||||
var recurse []reflect.Value
|
var recurse []reflect.Value
|
||||||
for _, dstValue := range dstValues {
|
// Use an iteration loop so elements can be added to the end of dstValues inside the loop.
|
||||||
|
for j := 0; j < len(dstValues); j++ {
|
||||||
|
dstValue := dstValues[j]
|
||||||
dstType := dstValue.Type()
|
dstType := dstValue.Type()
|
||||||
var dstField reflect.StructField
|
var dstField reflect.StructField
|
||||||
|
|
||||||
|
@ -297,6 +301,27 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
||||||
if field.Name == srcField.Name {
|
if field.Name == srcField.Name {
|
||||||
dstField = field
|
dstField = field
|
||||||
ok = true
|
ok = true
|
||||||
|
} else if field.Name == "BlueprintEmbed" || field.Anonymous {
|
||||||
|
embeddedDstValue := dstValue.FieldByIndex(field.Index)
|
||||||
|
if isStructPtr(embeddedDstValue.Type()) {
|
||||||
|
if embeddedDstValue.IsNil() {
|
||||||
|
newEmbeddedDstValue := reflect.New(embeddedDstValue.Type().Elem())
|
||||||
|
embeddedDstValue.Set(newEmbeddedDstValue)
|
||||||
|
}
|
||||||
|
embeddedDstValue = embeddedDstValue.Elem()
|
||||||
|
}
|
||||||
|
if !isStruct(embeddedDstValue.Type()) {
|
||||||
|
return extendPropertyErrorf(propertyName, "%s is not a struct (%s)",
|
||||||
|
prefix+field.Name, embeddedDstValue.Type())
|
||||||
|
}
|
||||||
|
// The destination struct contains an embedded struct, add it to the list
|
||||||
|
// of destinations to consider. Make a copy of dstValues if necessary
|
||||||
|
// to avoid modifying the backing array of an input parameter.
|
||||||
|
if !dstValuesCopied {
|
||||||
|
dstValues = append([]reflect.Value(nil), dstValues...)
|
||||||
|
dstValuesCopied = true
|
||||||
|
}
|
||||||
|
dstValues = append(dstValues, embeddedDstValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -813,6 +813,54 @@ func appendPropertiesTestCases() []appendPropertyTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "BlueprintEmbed struct",
|
||||||
|
dst: &struct {
|
||||||
|
BlueprintEmbed EmbeddedStruct
|
||||||
|
Nested struct{ BlueprintEmbed EmbeddedStruct }
|
||||||
|
}{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string1",
|
||||||
|
I: Int64Ptr(55),
|
||||||
|
},
|
||||||
|
Nested: struct{ BlueprintEmbed EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string2",
|
||||||
|
I: Int64Ptr(-4),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct {
|
||||||
|
BlueprintEmbed EmbeddedStruct
|
||||||
|
Nested struct{ BlueprintEmbed EmbeddedStruct }
|
||||||
|
}{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string3",
|
||||||
|
I: Int64Ptr(66),
|
||||||
|
},
|
||||||
|
Nested: struct{ BlueprintEmbed EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string4",
|
||||||
|
I: Int64Ptr(-8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
out: &struct {
|
||||||
|
BlueprintEmbed EmbeddedStruct
|
||||||
|
Nested struct{ BlueprintEmbed EmbeddedStruct }
|
||||||
|
}{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string1string3",
|
||||||
|
I: Int64Ptr(66),
|
||||||
|
},
|
||||||
|
Nested: struct{ BlueprintEmbed EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string2string4",
|
||||||
|
I: Int64Ptr(-8),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Anonymous interface",
|
name: "Anonymous interface",
|
||||||
dst: &struct {
|
dst: &struct {
|
||||||
|
@ -1476,6 +1524,130 @@ func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "Append through embedded struct",
|
||||||
|
dst: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ EmbeddedStruct }{
|
||||||
|
EmbeddedStruct: EmbeddedStruct{
|
||||||
|
S: "string1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct{ S string }{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
out: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ EmbeddedStruct }{
|
||||||
|
EmbeddedStruct: EmbeddedStruct{
|
||||||
|
S: "string1string2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append through BlueprintEmbed struct",
|
||||||
|
dst: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ BlueprintEmbed EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct{ S string }{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
out: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ BlueprintEmbed EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: EmbeddedStruct{
|
||||||
|
S: "string1string2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append through embedded pointer to struct",
|
||||||
|
dst: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ *EmbeddedStruct }{
|
||||||
|
EmbeddedStruct: &EmbeddedStruct{
|
||||||
|
S: "string1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct{ S string }{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
out: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ *EmbeddedStruct }{
|
||||||
|
EmbeddedStruct: &EmbeddedStruct{
|
||||||
|
S: "string1string2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append through BlueprintEmbed pointer to struct",
|
||||||
|
dst: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ BlueprintEmbed *EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: &EmbeddedStruct{
|
||||||
|
S: "string1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
src: &struct{ S string }{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
out: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ BlueprintEmbed *EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: &EmbeddedStruct{
|
||||||
|
S: "string1string2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append through embedded nil pointer to struct",
|
||||||
|
dst: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ *EmbeddedStruct }{},
|
||||||
|
},
|
||||||
|
src: &struct{ S string }{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
out: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ *EmbeddedStruct }{
|
||||||
|
EmbeddedStruct: &EmbeddedStruct{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Append through BlueprintEmbed nil pointer to struct",
|
||||||
|
dst: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ BlueprintEmbed *EmbeddedStruct }{},
|
||||||
|
},
|
||||||
|
src: &struct{ S string }{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
out: []interface{}{
|
||||||
|
&struct{ B string }{},
|
||||||
|
&struct{ BlueprintEmbed *EmbeddedStruct }{
|
||||||
|
BlueprintEmbed: &EmbeddedStruct{
|
||||||
|
S: "string2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// Errors
|
// Errors
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue