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,
|
||||
orderFunc ExtendPropertyOrderFunc) error {
|
||||
|
||||
dstValuesCopied := false
|
||||
|
||||
srcType := srcValue.Type()
|
||||
for i, srcField := range typeFields(srcType) {
|
||||
if srcField.PkgPath != "" {
|
||||
|
@ -284,7 +286,9 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
|||
|
||||
found := false
|
||||
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()
|
||||
var dstField reflect.StructField
|
||||
|
||||
|
@ -297,6 +301,27 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
|
|||
if field.Name == srcField.Name {
|
||||
dstField = field
|
||||
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 {
|
||||
|
|
|
@ -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",
|
||||
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
|
||||
|
||||
|
|
Loading…
Reference in a new issue