Merge pull request #99 from colincross/static
Support ExtendProperties that can append or prepend
diff --git a/proptools/extend.go b/proptools/extend.go
index 6ebbff3..3432755 100644
--- a/proptools/extend.go
+++ b/proptools/extend.go
@@ -34,7 +34,7 @@
// embedded structs, pointers to structs, and interfaces containing
// pointers to structs. Appending the zero value of a property will always be a no-op.
func AppendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error {
- return extendProperties(dst, src, filter, false)
+ return extendProperties(dst, src, filter, orderAppend)
}
// PrependProperties prepends the values of properties in the property struct src to the property
@@ -52,7 +52,7 @@
// embedded structs, pointers to structs, and interfaces containing
// pointers to structs. Prepending the zero value of a property will always be a no-op.
func PrependProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error {
- return extendProperties(dst, src, filter, true)
+ return extendProperties(dst, src, filter, orderPrepend)
}
// AppendMatchingProperties appends the values of properties in the property struct src to the
@@ -73,7 +73,7 @@
// pointers to structs. Appending the zero value of a property will always be a no-op.
func AppendMatchingProperties(dst []interface{}, src interface{},
filter ExtendPropertyFilterFunc) error {
- return extendMatchingProperties(dst, src, filter, false)
+ return extendMatchingProperties(dst, src, filter, orderAppend)
}
// PrependMatchingProperties prepends the values of properties in the property struct src to the
@@ -94,13 +94,84 @@
// pointers to structs. Prepending the zero value of a property will always be a no-op.
func PrependMatchingProperties(dst []interface{}, src interface{},
filter ExtendPropertyFilterFunc) error {
- return extendMatchingProperties(dst, src, filter, true)
+ return extendMatchingProperties(dst, src, filter, orderPrepend)
}
+// ExtendProperties appends or prepends the values of properties in the property struct src to the
+// property struct dst. dst and src must be the same type, and both must be pointers to structs.
+//
+// The filter function can prevent individual properties from being appended or prepended by
+// returning false, or abort ExtendProperties with an error by returning an error. Passing nil for
+// filter will append or prepend all properties.
+//
+// The order function is called on each non-filtered property to determine if it should be appended
+// or prepended.
+//
+// An error returned by ExtendProperties that applies to a specific property will be an
+// *ExtendPropertyError, and can have the property name and error extracted from it.
+//
+// The append operation is defined as appending strings and slices of strings normally, OR-ing bool
+// values, replacing non-nil pointers to booleans or strings, and recursing into
+// embedded structs, pointers to structs, and interfaces containing
+// pointers to structs. Appending or prepending the zero value of a property will always be a
+// no-op.
+func ExtendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc,
+ order ExtendPropertyOrderFunc) error {
+ return extendProperties(dst, src, filter, order)
+}
+
+// ExtendMatchingProperties appends or prepends the values of properties in the property struct src
+// to the property structs in dst. dst and src do not have to be the same type, but every property
+// in src must be found in at least one property in dst. dst must be a slice of pointers to
+// structs, and src must be a pointer to a struct.
+//
+// The filter function can prevent individual properties from being appended or prepended by
+// returning false, or abort ExtendMatchingProperties with an error by returning an error. Passing
+// nil for filter will append or prepend all properties.
+//
+// The order function is called on each non-filtered property to determine if it should be appended
+// or prepended.
+//
+// An error returned by ExtendMatchingProperties that applies to a specific property will be an
+// *ExtendPropertyError, and can have the property name and error extracted from it.
+//
+// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool
+// values, replacing non-nil pointers to booleans or strings, and recursing into
+// embedded structs, pointers to structs, and interfaces containing
+// pointers to structs. Appending or prepending the zero value of a property will always be a
+// no-op.
+func ExtendMatchingProperties(dst []interface{}, src interface{},
+ filter ExtendPropertyFilterFunc, order ExtendPropertyOrderFunc) error {
+ return extendMatchingProperties(dst, src, filter, order)
+}
+
+type Order int
+
+const (
+ Append Order = iota
+ Prepend
+)
+
type ExtendPropertyFilterFunc func(property string,
dstField, srcField reflect.StructField,
dstValue, srcValue interface{}) (bool, error)
+type ExtendPropertyOrderFunc func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (Order, error)
+
+func orderAppend(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (Order, error) {
+ return Append, nil
+}
+
+func orderPrepend(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (Order, error) {
+ return Prepend, nil
+}
+
type ExtendPropertyError struct {
Err error
Property string
@@ -118,7 +189,7 @@
}
func extendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc,
- prepend bool) error {
+ order ExtendPropertyOrderFunc) error {
dstValue, err := getStruct(dst)
if err != nil {
@@ -135,11 +206,11 @@
dstValues := []reflect.Value{dstValue}
- return extendPropertiesRecursive(dstValues, srcValue, "", filter, true, prepend)
+ return extendPropertiesRecursive(dstValues, srcValue, "", filter, true, order)
}
func extendMatchingProperties(dst []interface{}, src interface{}, filter ExtendPropertyFilterFunc,
- prepend bool) error {
+ order ExtendPropertyOrderFunc) error {
dstValues := make([]reflect.Value, len(dst))
for i := range dst {
@@ -155,11 +226,12 @@
return err
}
- return extendPropertiesRecursive(dstValues, srcValue, "", filter, false, prepend)
+ return extendPropertiesRecursive(dstValues, srcValue, "", filter, false, order)
}
func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value,
- prefix string, filter ExtendPropertyFilterFunc, sameTypes, prepend bool) error {
+ prefix string, filter ExtendPropertyFilterFunc, sameTypes bool,
+ order ExtendPropertyOrderFunc) error {
srcType := srcValue.Type()
for i := 0; i < srcValue.NumField(); i++ {
@@ -248,7 +320,7 @@
// Recursively extend the struct's fields.
err := extendPropertiesRecursive([]reflect.Value{dstFieldValue}, srcFieldValue,
- propertyName+".", filter, sameTypes, prepend)
+ propertyName+".", filter, sameTypes, order)
if err != nil {
return err
}
@@ -277,6 +349,19 @@
}
}
+ prepend := false
+ if order != nil {
+ b, err := order(propertyName, dstField, srcField,
+ dstFieldValue.Interface(), srcFieldValue.Interface())
+ if err != nil {
+ return &ExtendPropertyError{
+ Property: propertyName,
+ Err: err,
+ }
+ }
+ prepend = b == Prepend
+ }
+
switch srcFieldValue.Kind() {
case reflect.Bool:
// Boolean OR
diff --git a/proptools/extend_test.go b/proptools/extend_test.go
index edf28fd..0acd139 100644
--- a/proptools/extend_test.go
+++ b/proptools/extend_test.go
@@ -22,732 +22,736 @@
"testing"
)
-var appendPropertiesTestCases = []struct {
+type appendPropertyTestCase struct {
in1 interface{}
in2 interface{}
out interface{}
prepend bool
filter ExtendPropertyFilterFunc
err error
-}{
- // Valid inputs
+}
- {
- // Append bool
- in1: &struct{ B1, B2, B3, B4 bool }{
- B1: true,
- B2: false,
- B3: true,
- B4: false,
+func appendPropertiesTestCases() []appendPropertyTestCase {
+ return []appendPropertyTestCase{
+ // Valid inputs
+
+ {
+ // Append bool
+ in1: &struct{ B1, B2, B3, B4 bool }{
+ B1: true,
+ B2: false,
+ B3: true,
+ B4: false,
+ },
+ in2: &struct{ B1, B2, B3, B4 bool }{
+ B1: true,
+ B2: true,
+ B3: false,
+ B4: false,
+ },
+ out: &struct{ B1, B2, B3, B4 bool }{
+ B1: true,
+ B2: true,
+ B3: true,
+ B4: false,
+ },
},
- in2: &struct{ B1, B2, B3, B4 bool }{
- B1: true,
- B2: true,
- B3: false,
- B4: false,
+ {
+ // Prepend bool
+ in1: &struct{ B1, B2, B3, B4 bool }{
+ B1: true,
+ B2: false,
+ B3: true,
+ B4: false,
+ },
+ in2: &struct{ B1, B2, B3, B4 bool }{
+ B1: true,
+ B2: true,
+ B3: false,
+ B4: false,
+ },
+ out: &struct{ B1, B2, B3, B4 bool }{
+ B1: true,
+ B2: true,
+ B3: true,
+ B4: false,
+ },
+ prepend: true,
},
- out: &struct{ B1, B2, B3, B4 bool }{
- B1: true,
- B2: true,
- B3: true,
- B4: false,
- },
- },
- {
- // Prepend bool
- in1: &struct{ B1, B2, B3, B4 bool }{
- B1: true,
- B2: false,
- B3: true,
- B4: false,
- },
- in2: &struct{ B1, B2, B3, B4 bool }{
- B1: true,
- B2: true,
- B3: false,
- B4: false,
- },
- out: &struct{ B1, B2, B3, B4 bool }{
- B1: true,
- B2: true,
- B3: true,
- B4: false,
- },
- prepend: true,
- },
- {
- // Append strings
- in1: &struct{ S string }{
- S: "string1",
- },
- in2: &struct{ S string }{
- S: "string2",
- },
- out: &struct{ S string }{
- S: "string1string2",
- },
- },
- {
- // Prepend strings
- in1: &struct{ S string }{
- S: "string1",
- },
- in2: &struct{ S string }{
- S: "string2",
- },
- out: &struct{ S string }{
- S: "string2string1",
- },
- prepend: true,
- },
- {
- // Append pointer to bool
- in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
- B1: BoolPtr(true),
- B2: BoolPtr(false),
- B3: nil,
- B4: BoolPtr(true),
- B5: BoolPtr(false),
- B6: nil,
- B7: BoolPtr(true),
- B8: BoolPtr(false),
- B9: nil,
- },
- in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
- B1: nil,
- B2: nil,
- B3: nil,
- B4: BoolPtr(true),
- B5: BoolPtr(true),
- B6: BoolPtr(true),
- B7: BoolPtr(false),
- B8: BoolPtr(false),
- B9: BoolPtr(false),
- },
- out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
- B1: BoolPtr(true),
- B2: BoolPtr(false),
- B3: nil,
- B4: BoolPtr(true),
- B5: BoolPtr(true),
- B6: BoolPtr(true),
- B7: BoolPtr(false),
- B8: BoolPtr(false),
- B9: BoolPtr(false),
- },
- },
- {
- // Prepend pointer to bool
- in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
- B1: BoolPtr(true),
- B2: BoolPtr(false),
- B3: nil,
- B4: BoolPtr(true),
- B5: BoolPtr(false),
- B6: nil,
- B7: BoolPtr(true),
- B8: BoolPtr(false),
- B9: nil,
- },
- in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
- B1: nil,
- B2: nil,
- B3: nil,
- B4: BoolPtr(true),
- B5: BoolPtr(true),
- B6: BoolPtr(true),
- B7: BoolPtr(false),
- B8: BoolPtr(false),
- B9: BoolPtr(false),
- },
- out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
- B1: BoolPtr(true),
- B2: BoolPtr(false),
- B3: nil,
- B4: BoolPtr(true),
- B5: BoolPtr(false),
- B6: BoolPtr(true),
- B7: BoolPtr(true),
- B8: BoolPtr(false),
- B9: BoolPtr(false),
- },
- prepend: true,
- },
- {
- // Append pointer to strings
- in1: &struct{ S1, S2, S3, S4 *string }{
- S1: StringPtr("string1"),
- S2: StringPtr("string2"),
- },
- in2: &struct{ S1, S2, S3, S4 *string }{
- S1: StringPtr("string3"),
- S3: StringPtr("string4"),
- },
- out: &struct{ S1, S2, S3, S4 *string }{
- S1: StringPtr("string3"),
- S2: StringPtr("string2"),
- S3: StringPtr("string4"),
- S4: nil,
- },
- },
- {
- // Prepend pointer to strings
- in1: &struct{ S1, S2, S3, S4 *string }{
- S1: StringPtr("string1"),
- S2: StringPtr("string2"),
- },
- in2: &struct{ S1, S2, S3, S4 *string }{
- S1: StringPtr("string3"),
- S3: StringPtr("string4"),
- },
- out: &struct{ S1, S2, S3, S4 *string }{
- S1: StringPtr("string1"),
- S2: StringPtr("string2"),
- S3: StringPtr("string4"),
- S4: nil,
- },
- prepend: true,
- },
- {
- // Append slice
- in1: &struct{ S []string }{
- S: []string{"string1"},
- },
- in2: &struct{ S []string }{
- S: []string{"string2"},
- },
- out: &struct{ S []string }{
- S: []string{"string1", "string2"},
- },
- },
- {
- // Prepend slice
- in1: &struct{ S []string }{
- S: []string{"string1"},
- },
- in2: &struct{ S []string }{
- S: []string{"string2"},
- },
- out: &struct{ S []string }{
- S: []string{"string2", "string1"},
- },
- prepend: true,
- },
- {
- // Append empty slice
- in1: &struct{ S1, S2 []string }{
- S1: []string{"string1"},
- S2: []string{},
- },
- in2: &struct{ S1, S2 []string }{
- S1: []string{},
- S2: []string{"string2"},
- },
- out: &struct{ S1, S2 []string }{
- S1: []string{"string1"},
- S2: []string{"string2"},
- },
- },
- {
- // Prepend empty slice
- in1: &struct{ S1, S2 []string }{
- S1: []string{"string1"},
- S2: []string{},
- },
- in2: &struct{ S1, S2 []string }{
- S1: []string{},
- S2: []string{"string2"},
- },
- out: &struct{ S1, S2 []string }{
- S1: []string{"string1"},
- S2: []string{"string2"},
- },
- prepend: true,
- },
- {
- // Append nil slice
- in1: &struct{ S1, S2, S3 []string }{
- S1: []string{"string1"},
- },
- in2: &struct{ S1, S2, S3 []string }{
- S2: []string{"string2"},
- },
- out: &struct{ S1, S2, S3 []string }{
- S1: []string{"string1"},
- S2: []string{"string2"},
- S3: nil,
- },
- },
- {
- // Prepend nil slice
- in1: &struct{ S1, S2, S3 []string }{
- S1: []string{"string1"},
- },
- in2: &struct{ S1, S2, S3 []string }{
- S2: []string{"string2"},
- },
- out: &struct{ S1, S2, S3 []string }{
- S1: []string{"string1"},
- S2: []string{"string2"},
- S3: nil,
- },
- prepend: true,
- },
- {
- // Append pointer
- in1: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ {
+ // Append strings
+ in1: &struct{ S string }{
S: "string1",
},
- },
- in2: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ in2: &struct{ S string }{
S: "string2",
},
- },
- out: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ out: &struct{ S string }{
S: "string1string2",
},
},
- },
- {
- // Prepend pointer
- in1: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ {
+ // Prepend strings
+ in1: &struct{ S string }{
S: "string1",
},
- },
- in2: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ in2: &struct{ S string }{
S: "string2",
},
- },
- out: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ out: &struct{ S string }{
S: "string2string1",
},
+ prepend: true,
},
- prepend: true,
- },
- {
- // Append interface
- in1: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string1",
+ {
+ // Append pointer to bool
+ in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
+ B1: BoolPtr(true),
+ B2: BoolPtr(false),
+ B3: nil,
+ B4: BoolPtr(true),
+ B5: BoolPtr(false),
+ B6: nil,
+ B7: BoolPtr(true),
+ B8: BoolPtr(false),
+ B9: nil,
+ },
+ in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
+ B1: nil,
+ B2: nil,
+ B3: nil,
+ B4: BoolPtr(true),
+ B5: BoolPtr(true),
+ B6: BoolPtr(true),
+ B7: BoolPtr(false),
+ B8: BoolPtr(false),
+ B9: BoolPtr(false),
+ },
+ out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
+ B1: BoolPtr(true),
+ B2: BoolPtr(false),
+ B3: nil,
+ B4: BoolPtr(true),
+ B5: BoolPtr(true),
+ B6: BoolPtr(true),
+ B7: BoolPtr(false),
+ B8: BoolPtr(false),
+ B9: BoolPtr(false),
},
},
- in2: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string2",
+ {
+ // Prepend pointer to bool
+ in1: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
+ B1: BoolPtr(true),
+ B2: BoolPtr(false),
+ B3: nil,
+ B4: BoolPtr(true),
+ B5: BoolPtr(false),
+ B6: nil,
+ B7: BoolPtr(true),
+ B8: BoolPtr(false),
+ B9: nil,
+ },
+ in2: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
+ B1: nil,
+ B2: nil,
+ B3: nil,
+ B4: BoolPtr(true),
+ B5: BoolPtr(true),
+ B6: BoolPtr(true),
+ B7: BoolPtr(false),
+ B8: BoolPtr(false),
+ B9: BoolPtr(false),
+ },
+ out: &struct{ B1, B2, B3, B4, B5, B6, B7, B8, B9 *bool }{
+ B1: BoolPtr(true),
+ B2: BoolPtr(false),
+ B3: nil,
+ B4: BoolPtr(true),
+ B5: BoolPtr(false),
+ B6: BoolPtr(true),
+ B7: BoolPtr(true),
+ B8: BoolPtr(false),
+ B9: BoolPtr(false),
+ },
+ prepend: true,
+ },
+ {
+ // Append pointer to strings
+ in1: &struct{ S1, S2, S3, S4 *string }{
+ S1: StringPtr("string1"),
+ S2: StringPtr("string2"),
+ },
+ in2: &struct{ S1, S2, S3, S4 *string }{
+ S1: StringPtr("string3"),
+ S3: StringPtr("string4"),
+ },
+ out: &struct{ S1, S2, S3, S4 *string }{
+ S1: StringPtr("string3"),
+ S2: StringPtr("string2"),
+ S3: StringPtr("string4"),
+ S4: nil,
},
},
- out: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string1string2",
+ {
+ // Prepend pointer to strings
+ in1: &struct{ S1, S2, S3, S4 *string }{
+ S1: StringPtr("string1"),
+ S2: StringPtr("string2"),
+ },
+ in2: &struct{ S1, S2, S3, S4 *string }{
+ S1: StringPtr("string3"),
+ S3: StringPtr("string4"),
+ },
+ out: &struct{ S1, S2, S3, S4 *string }{
+ S1: StringPtr("string1"),
+ S2: StringPtr("string2"),
+ S3: StringPtr("string4"),
+ S4: nil,
+ },
+ prepend: true,
+ },
+ {
+ // Append slice
+ in1: &struct{ S []string }{
+ S: []string{"string1"},
+ },
+ in2: &struct{ S []string }{
+ S: []string{"string2"},
+ },
+ out: &struct{ S []string }{
+ S: []string{"string1", "string2"},
},
},
- },
- {
- // Prepend interface
- in1: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string1",
+ {
+ // Prepend slice
+ in1: &struct{ S []string }{
+ S: []string{"string1"},
+ },
+ in2: &struct{ S []string }{
+ S: []string{"string2"},
+ },
+ out: &struct{ S []string }{
+ S: []string{"string2", "string1"},
+ },
+ prepend: true,
+ },
+ {
+ // Append empty slice
+ in1: &struct{ S1, S2 []string }{
+ S1: []string{"string1"},
+ S2: []string{},
+ },
+ in2: &struct{ S1, S2 []string }{
+ S1: []string{},
+ S2: []string{"string2"},
+ },
+ out: &struct{ S1, S2 []string }{
+ S1: []string{"string1"},
+ S2: []string{"string2"},
},
},
- in2: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string2",
+ {
+ // Prepend empty slice
+ in1: &struct{ S1, S2 []string }{
+ S1: []string{"string1"},
+ S2: []string{},
+ },
+ in2: &struct{ S1, S2 []string }{
+ S1: []string{},
+ S2: []string{"string2"},
+ },
+ out: &struct{ S1, S2 []string }{
+ S1: []string{"string1"},
+ S2: []string{"string2"},
+ },
+ prepend: true,
+ },
+ {
+ // Append nil slice
+ in1: &struct{ S1, S2, S3 []string }{
+ S1: []string{"string1"},
+ },
+ in2: &struct{ S1, S2, S3 []string }{
+ S2: []string{"string2"},
+ },
+ out: &struct{ S1, S2, S3 []string }{
+ S1: []string{"string1"},
+ S2: []string{"string2"},
+ S3: nil,
},
},
- out: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string2string1",
+ {
+ // Prepend nil slice
+ in1: &struct{ S1, S2, S3 []string }{
+ S1: []string{"string1"},
},
- },
- prepend: true,
- },
- {
- // Unexported field
- in1: &struct{ s string }{
- s: "string1",
- },
- in2: &struct{ s string }{
- s: "string2",
- },
- out: &struct{ s string }{
- s: "string1",
- },
- },
- {
- // Empty struct
- in1: &struct{}{},
- in2: &struct{}{},
- out: &struct{}{},
- },
- {
- // Interface nil
- in1: &struct{ S interface{} }{
- S: nil,
- },
- in2: &struct{ S interface{} }{
- S: nil,
- },
- out: &struct{ S interface{} }{
- S: nil,
- },
- },
- {
- // Pointer nil
- in1: &struct{ S *struct{} }{
- S: nil,
- },
- in2: &struct{ S *struct{} }{
- S: nil,
- },
- out: &struct{ S *struct{} }{
- S: nil,
- },
- },
- {
- // Anonymous struct
- in1: &struct {
- EmbeddedStruct
- Nested struct{ EmbeddedStruct }
- }{
- EmbeddedStruct: EmbeddedStruct{
- S: "string1",
+ in2: &struct{ S1, S2, S3 []string }{
+ S2: []string{"string2"},
},
- Nested: struct{ EmbeddedStruct }{
- EmbeddedStruct: EmbeddedStruct{
+ out: &struct{ S1, S2, S3 []string }{
+ S1: []string{"string1"},
+ S2: []string{"string2"},
+ S3: nil,
+ },
+ prepend: true,
+ },
+ {
+ // Append pointer
+ in1: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
+ },
+ in2: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
S: "string2",
},
},
- },
- in2: &struct {
- EmbeddedStruct
- Nested struct{ EmbeddedStruct }
- }{
- EmbeddedStruct: EmbeddedStruct{
- S: "string3",
- },
- Nested: struct{ EmbeddedStruct }{
- EmbeddedStruct: EmbeddedStruct{
- S: "string4",
+ out: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string1string2",
},
},
},
- out: &struct {
- EmbeddedStruct
- Nested struct{ EmbeddedStruct }
- }{
- EmbeddedStruct: EmbeddedStruct{
- S: "string1string3",
- },
- Nested: struct{ EmbeddedStruct }{
- EmbeddedStruct: EmbeddedStruct{
- S: "string2string4",
+ {
+ // Prepend pointer
+ in1: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string1",
},
},
- },
- },
- {
- // Anonymous interface
- in1: &struct {
- EmbeddedInterface
- Nested struct{ EmbeddedInterface }
- }{
- EmbeddedInterface: &struct{ S string }{
- S: "string1",
- },
- Nested: struct{ EmbeddedInterface }{
- EmbeddedInterface: &struct{ S string }{
+ in2: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
S: "string2",
},
},
- },
- in2: &struct {
- EmbeddedInterface
- Nested struct{ EmbeddedInterface }
- }{
- EmbeddedInterface: &struct{ S string }{
- S: "string3",
+ out: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string2string1",
+ },
},
- Nested: struct{ EmbeddedInterface }{
- EmbeddedInterface: &struct{ S string }{
- S: "string4",
+ prepend: true,
+ },
+ {
+ // Append interface
+ in1: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
+ },
+ in2: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string2",
+ },
+ },
+ out: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string1string2",
},
},
},
- out: &struct {
- EmbeddedInterface
- Nested struct{ EmbeddedInterface }
- }{
- EmbeddedInterface: &struct{ S string }{
- S: "string1string3",
+ {
+ // Prepend interface
+ in1: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
},
- Nested: struct{ EmbeddedInterface }{
- EmbeddedInterface: &struct{ S string }{
- S: "string2string4",
+ in2: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string2",
+ },
+ },
+ out: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string2string1",
+ },
+ },
+ prepend: true,
+ },
+ {
+ // Unexported field
+ in1: &struct{ s string }{
+ s: "string1",
+ },
+ in2: &struct{ s string }{
+ s: "string2",
+ },
+ out: &struct{ s string }{
+ s: "string1",
+ },
+ },
+ {
+ // Empty struct
+ in1: &struct{}{},
+ in2: &struct{}{},
+ out: &struct{}{},
+ },
+ {
+ // Interface nil
+ in1: &struct{ S interface{} }{
+ S: nil,
+ },
+ in2: &struct{ S interface{} }{
+ S: nil,
+ },
+ out: &struct{ S interface{} }{
+ S: nil,
+ },
+ },
+ {
+ // Pointer nil
+ in1: &struct{ S *struct{} }{
+ S: nil,
+ },
+ in2: &struct{ S *struct{} }{
+ S: nil,
+ },
+ out: &struct{ S *struct{} }{
+ S: nil,
+ },
+ },
+ {
+ // Anonymous struct
+ in1: &struct {
+ EmbeddedStruct
+ Nested struct{ EmbeddedStruct }
+ }{
+ EmbeddedStruct: EmbeddedStruct{
+ S: "string1",
+ },
+ Nested: struct{ EmbeddedStruct }{
+ EmbeddedStruct: EmbeddedStruct{
+ S: "string2",
+ },
+ },
+ },
+ in2: &struct {
+ EmbeddedStruct
+ Nested struct{ EmbeddedStruct }
+ }{
+ EmbeddedStruct: EmbeddedStruct{
+ S: "string3",
+ },
+ Nested: struct{ EmbeddedStruct }{
+ EmbeddedStruct: EmbeddedStruct{
+ S: "string4",
+ },
+ },
+ },
+ out: &struct {
+ EmbeddedStruct
+ Nested struct{ EmbeddedStruct }
+ }{
+ EmbeddedStruct: EmbeddedStruct{
+ S: "string1string3",
+ },
+ Nested: struct{ EmbeddedStruct }{
+ EmbeddedStruct: EmbeddedStruct{
+ S: "string2string4",
+ },
},
},
},
- },
-
- // Errors
-
- {
- // Non-pointer in1
- in1: struct{}{},
- err: errors.New("expected pointer to struct, got struct {}"),
- out: struct{}{},
- },
- {
- // Non-pointer in2
- in1: &struct{}{},
- in2: struct{}{},
- err: errors.New("expected pointer to struct, got struct {}"),
- out: &struct{}{},
- },
- {
- // Non-struct in1
- in1: &[]string{"bad"},
- err: errors.New("expected pointer to struct, got *[]string"),
- out: &[]string{"bad"},
- },
- {
- // Non-struct in2
- in1: &struct{}{},
- in2: &[]string{"bad"},
- err: errors.New("expected pointer to struct, got *[]string"),
- out: &struct{}{},
- },
- {
- // Mismatched types
- in1: &struct{ A string }{
- A: "string1",
- },
- in2: &struct{ B string }{
- B: "string2",
- },
- out: &struct{ A string }{
- A: "string1",
- },
- err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"),
- },
- {
- // Unsupported kind
- in1: &struct{ I int }{
- I: 1,
- },
- in2: &struct{ I int }{
- I: 2,
- },
- out: &struct{ I int }{
- I: 1,
- },
- err: extendPropertyErrorf("i", "unsupported kind int"),
- },
- {
- // Interface nilitude mismatch
- in1: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string1",
+ {
+ // Anonymous interface
+ in1: &struct {
+ EmbeddedInterface
+ Nested struct{ EmbeddedInterface }
+ }{
+ EmbeddedInterface: &struct{ S string }{
+ S: "string1",
+ },
+ Nested: struct{ EmbeddedInterface }{
+ EmbeddedInterface: &struct{ S string }{
+ S: "string2",
+ },
+ },
+ },
+ in2: &struct {
+ EmbeddedInterface
+ Nested struct{ EmbeddedInterface }
+ }{
+ EmbeddedInterface: &struct{ S string }{
+ S: "string3",
+ },
+ Nested: struct{ EmbeddedInterface }{
+ EmbeddedInterface: &struct{ S string }{
+ S: "string4",
+ },
+ },
+ },
+ out: &struct {
+ EmbeddedInterface
+ Nested struct{ EmbeddedInterface }
+ }{
+ EmbeddedInterface: &struct{ S string }{
+ S: "string1string3",
+ },
+ Nested: struct{ EmbeddedInterface }{
+ EmbeddedInterface: &struct{ S string }{
+ S: "string2string4",
+ },
+ },
},
},
- in2: &struct{ S interface{} }{
- S: nil,
+
+ // Errors
+
+ {
+ // Non-pointer in1
+ in1: struct{}{},
+ err: errors.New("expected pointer to struct, got struct {}"),
+ out: struct{}{},
},
- out: &struct{ S interface{} }{
- S: &struct{ S string }{
- S: "string1",
- },
+ {
+ // Non-pointer in2
+ in1: &struct{}{},
+ in2: struct{}{},
+ err: errors.New("expected pointer to struct, got struct {}"),
+ out: &struct{}{},
},
- err: extendPropertyErrorf("s", "nilitude mismatch"),
- },
- {
- // Interface type mismatch
- in1: &struct{ S interface{} }{
- S: &struct{ A string }{
+ {
+ // Non-struct in1
+ in1: &[]string{"bad"},
+ err: errors.New("expected pointer to struct, got *[]string"),
+ out: &[]string{"bad"},
+ },
+ {
+ // Non-struct in2
+ in1: &struct{}{},
+ in2: &[]string{"bad"},
+ err: errors.New("expected pointer to struct, got *[]string"),
+ out: &struct{}{},
+ },
+ {
+ // Mismatched types
+ in1: &struct{ A string }{
A: "string1",
},
- },
- in2: &struct{ S interface{} }{
- S: &struct{ B string }{
+ in2: &struct{ B string }{
B: "string2",
},
- },
- out: &struct{ S interface{} }{
- S: &struct{ A string }{
+ out: &struct{ A string }{
A: "string1",
},
+ err: errors.New("expected matching types for dst and src, got *struct { A string } and *struct { B string }"),
},
- err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"),
- },
- {
- // Interface not a pointer
- in1: &struct{ S interface{} }{
- S: struct{ S string }{
- S: "string1",
- },
- },
- in2: &struct{ S interface{} }{
- S: struct{ S string }{
- S: "string2",
- },
- },
- out: &struct{ S interface{} }{
- S: struct{ S string }{
- S: "string1",
- },
- },
- err: extendPropertyErrorf("s", "interface not a pointer"),
- },
- {
- // Pointer nilitude mismatch
- in1: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
- S: "string1",
- },
- },
- in2: &struct{ S *struct{ S string } }{
- S: nil,
- },
- out: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
- S: "string1",
- },
- },
- err: extendPropertyErrorf("s", "nilitude mismatch"),
- },
- {
- // Pointer not a struct
- in1: &struct{ S *[]string }{
- S: &[]string{"string1"},
- },
- in2: &struct{ S *[]string }{
- S: &[]string{"string2"},
- },
- out: &struct{ S *[]string }{
- S: &[]string{"string1"},
- },
- err: extendPropertyErrorf("s", "pointer is a slice"),
- },
- {
- // Error in nested struct
- in1: &struct{ S interface{} }{
- S: &struct{ I int }{
+ {
+ // Unsupported kind
+ in1: &struct{ I int }{
I: 1,
},
- },
- in2: &struct{ S interface{} }{
- S: &struct{ I int }{
+ in2: &struct{ I int }{
I: 2,
},
- },
- out: &struct{ S interface{} }{
- S: &struct{ I int }{
+ out: &struct{ I int }{
I: 1,
},
+ err: extendPropertyErrorf("i", "unsupported kind int"),
},
- err: extendPropertyErrorf("s.i", "unsupported kind int"),
- },
+ {
+ // Interface nilitude mismatch
+ in1: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
+ },
+ in2: &struct{ S interface{} }{
+ S: nil,
+ },
+ out: &struct{ S interface{} }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
+ },
+ err: extendPropertyErrorf("s", "nilitude mismatch"),
+ },
+ {
+ // Interface type mismatch
+ in1: &struct{ S interface{} }{
+ S: &struct{ A string }{
+ A: "string1",
+ },
+ },
+ in2: &struct{ S interface{} }{
+ S: &struct{ B string }{
+ B: "string2",
+ },
+ },
+ out: &struct{ S interface{} }{
+ S: &struct{ A string }{
+ A: "string1",
+ },
+ },
+ err: extendPropertyErrorf("s", "mismatched types struct { A string } and struct { B string }"),
+ },
+ {
+ // Interface not a pointer
+ in1: &struct{ S interface{} }{
+ S: struct{ S string }{
+ S: "string1",
+ },
+ },
+ in2: &struct{ S interface{} }{
+ S: struct{ S string }{
+ S: "string2",
+ },
+ },
+ out: &struct{ S interface{} }{
+ S: struct{ S string }{
+ S: "string1",
+ },
+ },
+ err: extendPropertyErrorf("s", "interface not a pointer"),
+ },
+ {
+ // Pointer nilitude mismatch
+ in1: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
+ },
+ in2: &struct{ S *struct{ S string } }{
+ S: nil,
+ },
+ out: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string1",
+ },
+ },
+ err: extendPropertyErrorf("s", "nilitude mismatch"),
+ },
+ {
+ // Pointer not a struct
+ in1: &struct{ S *[]string }{
+ S: &[]string{"string1"},
+ },
+ in2: &struct{ S *[]string }{
+ S: &[]string{"string2"},
+ },
+ out: &struct{ S *[]string }{
+ S: &[]string{"string1"},
+ },
+ err: extendPropertyErrorf("s", "pointer is a slice"),
+ },
+ {
+ // Error in nested struct
+ in1: &struct{ S interface{} }{
+ S: &struct{ I int }{
+ I: 1,
+ },
+ },
+ in2: &struct{ S interface{} }{
+ S: &struct{ I int }{
+ I: 2,
+ },
+ },
+ out: &struct{ S interface{} }{
+ S: &struct{ I int }{
+ I: 1,
+ },
+ },
+ err: extendPropertyErrorf("s.i", "unsupported kind int"),
+ },
- // Filters
+ // Filters
- {
- // Filter true
- in1: &struct{ S string }{
- S: "string1",
+ {
+ // Filter true
+ in1: &struct{ S string }{
+ S: "string1",
+ },
+ in2: &struct{ S string }{
+ S: "string2",
+ },
+ out: &struct{ S string }{
+ S: "string1string2",
+ },
+ filter: func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (bool, error) {
+ return true, nil
+ },
},
- in2: &struct{ S string }{
- S: "string2",
+ {
+ // Filter false
+ in1: &struct{ S string }{
+ S: "string1",
+ },
+ in2: &struct{ S string }{
+ S: "string2",
+ },
+ out: &struct{ S string }{
+ S: "string1",
+ },
+ filter: func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (bool, error) {
+ return false, nil
+ },
},
- out: &struct{ S string }{
- S: "string1string2",
+ {
+ // Filter check args
+ in1: &struct{ S string }{
+ S: "string1",
+ },
+ in2: &struct{ S string }{
+ S: "string2",
+ },
+ out: &struct{ S string }{
+ S: "string1string2",
+ },
+ filter: func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (bool, error) {
+ return property == "s" &&
+ dstField.Name == "S" && srcField.Name == "S" &&
+ dstValue.(string) == "string1" && srcValue.(string) == "string2", nil
+ },
},
- filter: func(property string,
- dstField, srcField reflect.StructField,
- dstValue, srcValue interface{}) (bool, error) {
- return true, nil
+ {
+ // Filter mutated
+ in1: &struct {
+ S string `blueprint:"mutated"`
+ }{
+ S: "string1",
+ },
+ in2: &struct {
+ S string `blueprint:"mutated"`
+ }{
+ S: "string2",
+ },
+ out: &struct {
+ S string `blueprint:"mutated"`
+ }{
+ S: "string1",
+ },
},
- },
- {
- // Filter false
- in1: &struct{ S string }{
- S: "string1",
+ {
+ // Filter error
+ in1: &struct{ S string }{
+ S: "string1",
+ },
+ in2: &struct{ S string }{
+ S: "string2",
+ },
+ out: &struct{ S string }{
+ S: "string1",
+ },
+ filter: func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (bool, error) {
+ return true, fmt.Errorf("filter error")
+ },
+ err: extendPropertyErrorf("s", "filter error"),
},
- in2: &struct{ S string }{
- S: "string2",
- },
- out: &struct{ S string }{
- S: "string1",
- },
- filter: func(property string,
- dstField, srcField reflect.StructField,
- dstValue, srcValue interface{}) (bool, error) {
- return false, nil
- },
- },
- {
- // Filter check args
- in1: &struct{ S string }{
- S: "string1",
- },
- in2: &struct{ S string }{
- S: "string2",
- },
- out: &struct{ S string }{
- S: "string1string2",
- },
- filter: func(property string,
- dstField, srcField reflect.StructField,
- dstValue, srcValue interface{}) (bool, error) {
- return property == "s" &&
- dstField.Name == "S" && srcField.Name == "S" &&
- dstValue.(string) == "string1" && srcValue.(string) == "string2", nil
- },
- },
- {
- // Filter mutated
- in1: &struct {
- S string `blueprint:"mutated"`
- }{
- S: "string1",
- },
- in2: &struct {
- S string `blueprint:"mutated"`
- }{
- S: "string2",
- },
- out: &struct {
- S string `blueprint:"mutated"`
- }{
- S: "string1",
- },
- },
- {
- // Filter error
- in1: &struct{ S string }{
- S: "string1",
- },
- in2: &struct{ S string }{
- S: "string2",
- },
- out: &struct{ S string }{
- S: "string1",
- },
- filter: func(property string,
- dstField, srcField reflect.StructField,
- dstValue, srcValue interface{}) (bool, error) {
- return true, fmt.Errorf("filter error")
- },
- err: extendPropertyErrorf("s", "filter error"),
- },
+ }
}
func TestAppendProperties(t *testing.T) {
- for _, testCase := range appendPropertiesTestCases {
+ for _, testCase := range appendPropertiesTestCases() {
testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out)
got := testCase.in1
@@ -766,191 +770,225 @@
}
}
-var appendMatchingPropertiesTestCases = []struct {
+func TestExtendProperties(t *testing.T) {
+ for _, testCase := range appendPropertiesTestCases() {
+ testString := fmt.Sprintf("%v, %v -> %v", testCase.in1, testCase.in2, testCase.out)
+
+ got := testCase.in1
+ var err error
+ var testType string
+
+ order := func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (Order, error) {
+ if testCase.prepend {
+ return Prepend, nil
+ } else {
+ return Append, nil
+ }
+ }
+
+ if testCase.prepend {
+ testType = "prepend"
+ } else {
+ testType = "append"
+ }
+
+ err = ExtendProperties(got, testCase.in2, testCase.filter, order)
+
+ check(t, testType, testString, got, err, testCase.out, testCase.err)
+ }
+}
+
+type appendMatchingPropertiesTestCase struct {
in1 []interface{}
in2 interface{}
out []interface{}
prepend bool
filter ExtendPropertyFilterFunc
err error
-}{
- {
- // Append strings
- in1: []interface{}{&struct{ S string }{
- S: "string1",
- }},
- in2: &struct{ S string }{
- S: "string2",
- },
- out: []interface{}{&struct{ S string }{
- S: "string1string2",
- }},
- },
- {
- // Prepend strings
- in1: []interface{}{&struct{ S string }{
- S: "string1",
- }},
- in2: &struct{ S string }{
- S: "string2",
- },
- out: []interface{}{&struct{ S string }{
- S: "string2string1",
- }},
- prepend: true,
- },
- {
- // Append all
- in1: []interface{}{
- &struct{ S, A string }{
+}
+
+func appendMatchingPropertiesTestCases() []appendMatchingPropertiesTestCase {
+ return []appendMatchingPropertiesTestCase{
+ {
+ // Append strings
+ in1: []interface{}{&struct{ S string }{
S: "string1",
- },
- &struct{ S, B string }{
+ }},
+ in2: &struct{ S string }{
S: "string2",
},
- },
- in2: &struct{ S string }{
- S: "string3",
- },
- out: []interface{}{
- &struct{ S, A string }{
- S: "string1string3",
- },
- &struct{ S, B string }{
- S: "string2string3",
- },
- },
- },
- {
- // Append some
- in1: []interface{}{
- &struct{ S, A string }{
- S: "string1",
- },
- &struct{ B string }{},
- },
- in2: &struct{ S string }{
- S: "string2",
- },
- out: []interface{}{
- &struct{ S, A string }{
+ out: []interface{}{&struct{ S string }{
S: "string1string2",
- },
- &struct{ B string }{},
+ }},
},
- },
- {
- // Append mismatched structs
- in1: []interface{}{&struct{ S, A string }{
- S: "string1",
- }},
- in2: &struct{ S string }{
- S: "string2",
- },
- out: []interface{}{&struct{ S, A string }{
- S: "string1string2",
- }},
- },
- {
- // Append mismatched pointer structs
- in1: []interface{}{&struct{ S *struct{ S, A string } }{
- S: &struct{ S, A string }{
+ {
+ // Prepend strings
+ in1: []interface{}{&struct{ S string }{
S: "string1",
- },
- }},
- in2: &struct{ S *struct{ S string } }{
- S: &struct{ S string }{
+ }},
+ in2: &struct{ S string }{
S: "string2",
},
+ out: []interface{}{&struct{ S string }{
+ S: "string2string1",
+ }},
+ prepend: true,
},
- out: []interface{}{&struct{ S *struct{ S, A string } }{
- S: &struct{ S, A string }{
+ {
+ // Append all
+ in1: []interface{}{
+ &struct{ S, A string }{
+ S: "string1",
+ },
+ &struct{ S, B string }{
+ S: "string2",
+ },
+ },
+ in2: &struct{ S string }{
+ S: "string3",
+ },
+ out: []interface{}{
+ &struct{ S, A string }{
+ S: "string1string3",
+ },
+ &struct{ S, B string }{
+ S: "string2string3",
+ },
+ },
+ },
+ {
+ // Append some
+ in1: []interface{}{
+ &struct{ S, A string }{
+ S: "string1",
+ },
+ &struct{ B string }{},
+ },
+ in2: &struct{ S string }{
+ S: "string2",
+ },
+ out: []interface{}{
+ &struct{ S, A string }{
+ S: "string1string2",
+ },
+ &struct{ B string }{},
+ },
+ },
+ {
+ // Append mismatched structs
+ in1: []interface{}{&struct{ S, A string }{
+ S: "string1",
+ }},
+ in2: &struct{ S string }{
+ S: "string2",
+ },
+ out: []interface{}{&struct{ S, A string }{
S: "string1string2",
+ }},
+ },
+ {
+ // Append mismatched pointer structs
+ in1: []interface{}{&struct{ S *struct{ S, A string } }{
+ S: &struct{ S, A string }{
+ S: "string1",
+ },
+ }},
+ in2: &struct{ S *struct{ S string } }{
+ S: &struct{ S string }{
+ S: "string2",
+ },
},
- }},
- },
+ out: []interface{}{&struct{ S *struct{ S, A string } }{
+ S: &struct{ S, A string }{
+ S: "string1string2",
+ },
+ }},
+ },
- // Errors
+ // Errors
- {
- // Non-pointer in1
- in1: []interface{}{struct{}{}},
- err: errors.New("expected pointer to struct, got struct {}"),
- out: []interface{}{struct{}{}},
- },
- {
- // Non-pointer in2
- in1: []interface{}{&struct{}{}},
- in2: struct{}{},
- err: errors.New("expected pointer to struct, got struct {}"),
- out: []interface{}{&struct{}{}},
- },
- {
- // Non-struct in1
- in1: []interface{}{&[]string{"bad"}},
- err: errors.New("expected pointer to struct, got *[]string"),
- out: []interface{}{&[]string{"bad"}},
- },
- {
- // Non-struct in2
- in1: []interface{}{&struct{}{}},
- in2: &[]string{"bad"},
- err: errors.New("expected pointer to struct, got *[]string"),
- out: []interface{}{&struct{}{}},
- },
- {
- // Append none
- in1: []interface{}{
- &struct{ A string }{},
- &struct{ B string }{},
+ {
+ // Non-pointer in1
+ in1: []interface{}{struct{}{}},
+ err: errors.New("expected pointer to struct, got struct {}"),
+ out: []interface{}{struct{}{}},
},
- in2: &struct{ S string }{
- S: "string1",
+ {
+ // Non-pointer in2
+ in1: []interface{}{&struct{}{}},
+ in2: struct{}{},
+ err: errors.New("expected pointer to struct, got struct {}"),
+ out: []interface{}{&struct{}{}},
},
- out: []interface{}{
- &struct{ A string }{},
- &struct{ B string }{},
+ {
+ // Non-struct in1
+ in1: []interface{}{&[]string{"bad"}},
+ err: errors.New("expected pointer to struct, got *[]string"),
+ out: []interface{}{&[]string{"bad"}},
},
- err: extendPropertyErrorf("s", "failed to find property to extend"),
- },
- {
- // Append mismatched kinds
- in1: []interface{}{
- &struct{ S string }{
+ {
+ // Non-struct in2
+ in1: []interface{}{&struct{}{}},
+ in2: &[]string{"bad"},
+ err: errors.New("expected pointer to struct, got *[]string"),
+ out: []interface{}{&struct{}{}},
+ },
+ {
+ // Append none
+ in1: []interface{}{
+ &struct{ A string }{},
+ &struct{ B string }{},
+ },
+ in2: &struct{ S string }{
S: "string1",
},
- },
- in2: &struct{ S []string }{
- S: []string{"string2"},
- },
- out: []interface{}{
- &struct{ S string }{
- S: "string1",
+ out: []interface{}{
+ &struct{ A string }{},
+ &struct{ B string }{},
},
+ err: extendPropertyErrorf("s", "failed to find property to extend"),
},
- err: extendPropertyErrorf("s", "mismatched types string and []string"),
- },
- {
- // Append mismatched types
- in1: []interface{}{
- &struct{ S []int }{
- S: []int{1},
+ {
+ // Append mismatched kinds
+ in1: []interface{}{
+ &struct{ S string }{
+ S: "string1",
+ },
},
- },
- in2: &struct{ S []string }{
- S: []string{"string2"},
- },
- out: []interface{}{
- &struct{ S []int }{
- S: []int{1},
+ in2: &struct{ S []string }{
+ S: []string{"string2"},
},
+ out: []interface{}{
+ &struct{ S string }{
+ S: "string1",
+ },
+ },
+ err: extendPropertyErrorf("s", "mismatched types string and []string"),
},
- err: extendPropertyErrorf("s", "mismatched types []int and []string"),
- },
+ {
+ // Append mismatched types
+ in1: []interface{}{
+ &struct{ S []int }{
+ S: []int{1},
+ },
+ },
+ in2: &struct{ S []string }{
+ S: []string{"string2"},
+ },
+ out: []interface{}{
+ &struct{ S []int }{
+ S: []int{1},
+ },
+ },
+ err: extendPropertyErrorf("s", "mismatched types []int and []string"),
+ },
+ }
}
func TestAppendMatchingProperties(t *testing.T) {
- for _, testCase := range appendMatchingPropertiesTestCases {
+ for _, testCase := range appendMatchingPropertiesTestCases() {
testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out))
got := testCase.in1
@@ -969,6 +1007,36 @@
}
}
+func TestExtendMatchingProperties(t *testing.T) {
+ for _, testCase := range appendMatchingPropertiesTestCases() {
+ testString := fmt.Sprintf("%s, %s -> %s", p(testCase.in1), p(testCase.in2), p(testCase.out))
+
+ got := testCase.in1
+ var err error
+ var testType string
+
+ order := func(property string,
+ dstField, srcField reflect.StructField,
+ dstValue, srcValue interface{}) (Order, error) {
+ if testCase.prepend {
+ return Prepend, nil
+ } else {
+ return Append, nil
+ }
+ }
+
+ if testCase.prepend {
+ testType = "prepend matching"
+ } else {
+ testType = "append matching"
+ }
+
+ err = ExtendMatchingProperties(got, testCase.in2, testCase.filter, order)
+
+ check(t, testType, testString, got, err, testCase.out, testCase.err)
+ }
+}
+
func check(t *testing.T, testType, testString string,
got interface{}, err error,
expected interface{}, expectedErr error) {