Replace unpack's replace semantics with append

Blueprint was using "replace" semantics when unpacking properties
into property structs, meaning if a module factory pre-set property
values they would be overwritten by whatever was in the Blueprint
file.  This is different than what would happen if the same property
was updated using the Append*Properties functions in proptools, which
would use "append" semantics, which append strings and lists,
logically ORs booleans and replaces pointers to strings and booleans.
Replace unpack's semantics with append semantics for consistency.
Any previous users of pre-set properties can move to using a pointer
to a string or boolean if they want the old behavior.

Test: unpack_test.go
Test: extend_test.go
Change-Id: I02eebe80916e578938142f8e76889bd985223afc
This commit is contained in:
Colin Cross 2017-07-28 17:51:37 -07:00
parent 5fe225f5f9
commit 05b3607c37
3 changed files with 179 additions and 129 deletions

View file

@ -238,7 +238,7 @@ func extendMatchingProperties(dst []interface{}, src interface{}, filter ExtendP
func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value, func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value,
prefix string, filter ExtendPropertyFilterFunc, sameTypes bool, prefix string, filter ExtendPropertyFilterFunc, sameTypes bool,
order ExtendPropertyOrderFunc) error { orderFunc ExtendPropertyOrderFunc) error {
srcType := srcValue.Type() srcType := srcValue.Type()
for i, srcField := range typeFields(srcType) { for i, srcField := range typeFields(srcType) {
@ -373,9 +373,10 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
} }
} }
prepend := false order := Append
if order != nil { if orderFunc != nil {
b, err := order(propertyName, dstField, srcField, var err error
order, err = orderFunc(propertyName, dstField, srcField,
dstFieldInterface, srcFieldInterface) dstFieldInterface, srcFieldInterface)
if err != nil { if err != nil {
return &ExtendPropertyError{ return &ExtendPropertyError{
@ -383,15 +384,33 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
Err: err, Err: err,
} }
} }
prepend = b == Prepend
} }
ExtendBasicType(dstFieldValue, srcFieldValue, order)
}
if len(recurse) > 0 {
err := extendPropertiesRecursive(recurse, srcFieldValue,
propertyName+".", filter, sameTypes, orderFunc)
if err != nil {
return err
}
} else if !found {
return extendPropertyErrorf(propertyName, "failed to find property to extend")
}
}
return nil
}
func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) {
prepend := order == Prepend
switch srcFieldValue.Kind() { switch srcFieldValue.Kind() {
case reflect.Bool: case reflect.Bool:
// Boolean OR // Boolean OR
dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Bool() || dstFieldValue.Bool())) dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Bool() || dstFieldValue.Bool()))
case reflect.String: case reflect.String:
// Append the extension string.
if prepend { if prepend {
dstFieldValue.SetString(srcFieldValue.String() + dstFieldValue.SetString(srcFieldValue.String() +
dstFieldValue.String()) dstFieldValue.String())
@ -442,19 +461,6 @@ func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value
panic(fmt.Errorf("unexpected pointer kind %s", ptrKind)) panic(fmt.Errorf("unexpected pointer kind %s", ptrKind))
} }
} }
}
if len(recurse) > 0 {
err := extendPropertiesRecursive(recurse, srcFieldValue,
propertyName+".", filter, sameTypes, order)
if err != nil {
return err
}
} else if !found {
return extendPropertyErrorf(propertyName, "failed to find property to extend")
}
}
return nil
} }
type getStructEmptyError struct{} type getStructEmptyError struct{}

101
unpack.go
View file

@ -240,27 +240,7 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
var newErrs []error var newErrs []error
switch kind := fieldValue.Kind(); kind { if fieldValue.Kind() == reflect.Struct {
case reflect.Bool:
newErrs = unpackBool(fieldValue, packedProperty.property)
case reflect.String:
newErrs = unpackString(fieldValue, packedProperty.property)
case reflect.Slice:
newErrs = unpackSlice(fieldValue, packedProperty.property)
case reflect.Ptr:
switch ptrKind := fieldValue.Type().Elem().Kind(); ptrKind {
case reflect.Bool:
newValue := reflect.New(fieldValue.Type().Elem())
newErrs = unpackBool(newValue.Elem(), packedProperty.property)
fieldValue.Set(newValue)
case reflect.String:
newValue := reflect.New(fieldValue.Type().Elem())
newErrs = unpackString(newValue.Elem(), packedProperty.property)
fieldValue.Set(newValue)
default:
panic(fmt.Errorf("unexpected pointer kind %s", ptrKind))
}
case reflect.Struct:
localFilterKey, localFilterValue := filterKey, filterValue localFilterKey, localFilterValue := filterKey, filterValue
if k, v, err := HasFilter(field.Tag); err != nil { if k, v, err := HasFilter(field.Tag); err != nil {
errs = append(errs, err) errs = append(errs, err)
@ -280,52 +260,62 @@ func unpackStructValue(namePrefix string, structValue reflect.Value,
} }
newErrs = unpackStruct(propertyName+".", fieldValue, newErrs = unpackStruct(propertyName+".", fieldValue,
packedProperty.property, propertyMap, localFilterKey, localFilterValue) packedProperty.property, propertyMap, localFilterKey, localFilterValue)
default:
panic(fmt.Errorf("unexpected kind %s", kind))
}
errs = append(errs, newErrs...) errs = append(errs, newErrs...)
if len(errs) >= maxErrors { if len(errs) >= maxErrors {
return errs return errs
} }
continue
}
// Handle basic types and pointers to basic types
propertyValue, err := propertyToValue(fieldValue.Type(), packedProperty.property)
if err != nil {
errs = append(errs, err)
if len(errs) >= maxErrors {
return errs
}
}
proptools.ExtendBasicType(fieldValue, propertyValue, proptools.Append)
} }
return errs return errs
} }
func unpackBool(boolValue reflect.Value, property *parser.Property) []error { func propertyToValue(typ reflect.Type, property *parser.Property) (reflect.Value, error) {
var value reflect.Value
var ptr bool
if typ.Kind() == reflect.Ptr {
typ = typ.Elem()
ptr = true
}
switch kind := typ.Kind(); kind {
case reflect.Bool:
b, ok := property.Value.Eval().(*parser.Bool) b, ok := property.Value.Eval().(*parser.Bool)
if !ok { if !ok {
return []error{ return value, fmt.Errorf("%s: can't assign %s value to bool property %q",
fmt.Errorf("%s: can't assign %s value to bool property %q", property.Value.Pos(), property.Value.Type(), property.Name)
property.Value.Pos(), property.Value.Type(), property.Name),
} }
} value = reflect.ValueOf(b.Value)
boolValue.SetBool(b.Value)
return nil
}
func unpackString(stringValue reflect.Value,
property *parser.Property) []error {
case reflect.String:
s, ok := property.Value.Eval().(*parser.String) s, ok := property.Value.Eval().(*parser.String)
if !ok { if !ok {
return []error{ return value, fmt.Errorf("%s: can't assign %s value to string property %q",
fmt.Errorf("%s: can't assign %s value to string property %q", property.Value.Pos(), property.Value.Type(), property.Name)
property.Value.Pos(), property.Value.Type(), property.Name),
} }
} value = reflect.ValueOf(s.Value)
stringValue.SetString(s.Value)
return nil
}
func unpackSlice(sliceValue reflect.Value, property *parser.Property) []error {
case reflect.Slice:
l, ok := property.Value.Eval().(*parser.List) l, ok := property.Value.Eval().(*parser.List)
if !ok { if !ok {
return []error{ return value, fmt.Errorf("%s: can't assign %s value to list property %q",
fmt.Errorf("%s: can't assign %s value to list property %q", property.Value.Pos(), property.Value.Type(), property.Name)
property.Value.Pos(), property.Value.Type(), property.Name),
}
} }
list := make([]string, len(l.Values)) list := make([]string, len(l.Values))
@ -338,8 +328,19 @@ func unpackSlice(sliceValue reflect.Value, property *parser.Property) []error {
list[i] = s.Value list[i] = s.Value
} }
sliceValue.Set(reflect.ValueOf(list)) value = reflect.ValueOf(list)
return nil
default:
panic(fmt.Errorf("unexpected kind %s", kind))
}
if ptr {
ptrValue := reflect.New(value.Type())
ptrValue.Elem().Set(value)
value = ptrValue
}
return value, nil
} }
func unpackStruct(namePrefix string, structValue reflect.Value, func unpackStruct(namePrefix string, structValue reflect.Value,

View file

@ -498,6 +498,49 @@ var validUnpackTestCases = []struct {
}, },
}, },
}, },
// Factory set properties
{
input: `
m {
string: "abc",
string_ptr: "abc",
bool: false,
bool_ptr: false,
list: ["a", "b", "c"],
}
`,
output: []interface{}{
struct {
String string
String_ptr *string
Bool bool
Bool_ptr *bool
List []string
}{
String: "012abc",
String_ptr: proptools.StringPtr("abc"),
Bool: true,
Bool_ptr: proptools.BoolPtr(false),
List: []string{"0", "1", "2", "a", "b", "c"},
},
},
empty: []interface{}{
&struct {
String string
String_ptr *string
Bool bool
Bool_ptr *bool
List []string
}{
String: "012",
String_ptr: proptools.StringPtr("012"),
Bool: true,
Bool_ptr: proptools.BoolPtr(true),
List: []string{"0", "1", "2"},
},
},
},
} }
type EmbeddedStruct struct{ Name string } type EmbeddedStruct struct{ Name string }