1// Copyright 2015 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package proptools 16 17import ( 18 "fmt" 19 "reflect" 20 "slices" 21 "strings" 22) 23 24// AppendProperties appends the values of properties in the property struct src to the property 25// struct dst. dst and src must be the same type, and both must be pointers to structs. Properties 26// tagged `blueprint:"mutated"` are skipped. 27// 28// The filter function can prevent individual properties from being appended by returning false, or 29// abort AppendProperties with an error by returning an error. Passing nil for filter will append 30// all properties. 31// 32// An error returned by AppendProperties that applies to a specific property will be an 33// *ExtendPropertyError, and can have the property name and error extracted from it. 34// 35// The append operation is defined as appending strings and slices of strings normally, OR-ing bool 36// values, replacing non-nil pointers to booleans or strings, and recursing into 37// embedded structs, pointers to structs, and interfaces containing 38// pointers to structs. Appending the zero value of a property will always be a no-op. 39func AppendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error { 40 return extendProperties(dst, src, filter, OrderAppend) 41} 42 43// PrependProperties prepends the values of properties in the property struct src to the property 44// struct dst. dst and src must be the same type, and both must be pointers to structs. Properties 45// tagged `blueprint:"mutated"` are skipped. 46// 47// The filter function can prevent individual properties from being prepended by returning false, or 48// abort PrependProperties with an error by returning an error. Passing nil for filter will prepend 49// all properties. 50// 51// An error returned by PrependProperties that applies to a specific property will be an 52// *ExtendPropertyError, and can have the property name and error extracted from it. 53// 54// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing 55// bool values, replacing non-nil pointers to booleans or strings, and recursing into 56// embedded structs, pointers to structs, and interfaces containing 57// pointers to structs. Prepending the zero value of a property will always be a no-op. 58func PrependProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error { 59 return extendProperties(dst, src, filter, OrderPrepend) 60} 61 62// AppendMatchingProperties appends the values of properties in the property struct src to the 63// property structs in dst. dst and src do not have to be the same type, but every property in src 64// must be found in at least one property in dst. dst must be a slice of pointers to structs, and 65// src must be a pointer to a struct. Properties tagged `blueprint:"mutated"` are skipped. 66// 67// The filter function can prevent individual properties from being appended by returning false, or 68// abort AppendProperties with an error by returning an error. Passing nil for filter will append 69// all properties. 70// 71// An error returned by AppendMatchingProperties that applies to a specific property will be an 72// *ExtendPropertyError, and can have the property name and error extracted from it. 73// 74// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool 75// values, replacing pointers to booleans or strings whether they are nil or not, and recursing into 76// embedded structs, pointers to structs, and interfaces containing 77// pointers to structs. Appending the zero value of a property will always be a no-op. 78func AppendMatchingProperties(dst []interface{}, src interface{}, 79 filter ExtendPropertyFilterFunc) error { 80 return extendMatchingProperties(dst, src, filter, OrderAppend) 81} 82 83// PrependMatchingProperties prepends the values of properties in the property struct src to the 84// property structs in dst. dst and src do not have to be the same type, but every property in src 85// must be found in at least one property in dst. dst must be a slice of pointers to structs, and 86// src must be a pointer to a struct. Properties tagged `blueprint:"mutated"` are skipped. 87// 88// The filter function can prevent individual properties from being prepended by returning false, or 89// abort PrependProperties with an error by returning an error. Passing nil for filter will prepend 90// all properties. 91// 92// An error returned by PrependProperties that applies to a specific property will be an 93// *ExtendPropertyError, and can have the property name and error extracted from it. 94// 95// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing 96// bool values, replacing nil pointers to booleans or strings, and recursing into 97// embedded structs, pointers to structs, and interfaces containing 98// pointers to structs. Prepending the zero value of a property will always be a no-op. 99func PrependMatchingProperties(dst []interface{}, src interface{}, 100 filter ExtendPropertyFilterFunc) error { 101 return extendMatchingProperties(dst, src, filter, OrderPrepend) 102} 103 104// ExtendProperties appends or prepends the values of properties in the property struct src to the 105// property struct dst. dst and src must be the same type, and both must be pointers to structs. 106// Properties tagged `blueprint:"mutated"` are skipped. 107// 108// The filter function can prevent individual properties from being appended or prepended by 109// returning false, or abort ExtendProperties with an error by returning an error. Passing nil for 110// filter will append or prepend all properties. 111// 112// The order function is called on each non-filtered property to determine if it should be appended 113// or prepended. 114// 115// An error returned by ExtendProperties that applies to a specific property will be an 116// *ExtendPropertyError, and can have the property name and error extracted from it. 117// 118// The append operation is defined as appending strings and slices of strings normally, OR-ing bool 119// values, replacing non-nil pointers to booleans or strings, and recursing into 120// embedded structs, pointers to structs, and interfaces containing 121// pointers to structs. Appending or prepending the zero value of a property will always be a 122// no-op. 123func ExtendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc, 124 order ExtendPropertyOrderFunc) error { 125 return extendProperties(dst, src, filter, order) 126} 127 128// ExtendMatchingProperties appends or prepends the values of properties in the property struct src 129// to the property structs in dst. dst and src do not have to be the same type, but every property 130// in src must be found in at least one property in dst. dst must be a slice of pointers to 131// structs, and src must be a pointer to a struct. Properties tagged `blueprint:"mutated"` are 132// skipped. 133// 134// The filter function can prevent individual properties from being appended or prepended by 135// returning false, or abort ExtendMatchingProperties with an error by returning an error. Passing 136// nil for filter will append or prepend all properties. 137// 138// The order function is called on each non-filtered property to determine if it should be appended 139// or prepended. 140// 141// An error returned by ExtendMatchingProperties that applies to a specific property will be an 142// *ExtendPropertyError, and can have the property name and error extracted from it. 143// 144// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool 145// values, replacing non-nil pointers to booleans or strings, and recursing into 146// embedded structs, pointers to structs, and interfaces containing 147// pointers to structs. Appending or prepending the zero value of a property will always be a 148// no-op. 149func ExtendMatchingProperties(dst []interface{}, src interface{}, 150 filter ExtendPropertyFilterFunc, order ExtendPropertyOrderFunc) error { 151 return extendMatchingProperties(dst, src, filter, order) 152} 153 154type Order int 155 156const ( 157 // When merging properties, strings and lists will be concatenated, and booleans will be OR'd together 158 Append Order = iota 159 // Same as append, but acts as if the arguments to the extend* functions were swapped. The src value will be 160 // prepended to the dst value instead of appended. 161 Prepend 162 // Instead of concatenating/ORing properties, the dst value will be completely replaced by the src value. 163 // Replace currently only works for slices, maps, and configurable properties. Due to legacy behavior, 164 // pointer properties will always act as if they're using replace ordering. 165 Replace 166 // Same as replace, but acts as if the arguments to the extend* functions were swapped. The src value will be 167 // used only if the dst value was unset. 168 Prepend_replace 169) 170 171type ExtendPropertyFilterFunc func(dstField, srcField reflect.StructField) (bool, error) 172 173type ExtendPropertyOrderFunc func(dstField, srcField reflect.StructField) (Order, error) 174 175func OrderAppend(dstField, srcField reflect.StructField) (Order, error) { 176 return Append, nil 177} 178 179func OrderPrepend(dstField, srcField reflect.StructField) (Order, error) { 180 return Prepend, nil 181} 182 183func OrderReplace(dstField, srcField reflect.StructField) (Order, error) { 184 return Replace, nil 185} 186 187type ExtendPropertyError struct { 188 Err error 189 Property string 190} 191 192func (e *ExtendPropertyError) Error() string { 193 return fmt.Sprintf("can't extend property %q: %s", e.Property, e.Err) 194} 195 196func extendPropertyErrorf(property string, format string, a ...interface{}) *ExtendPropertyError { 197 return &ExtendPropertyError{ 198 Err: fmt.Errorf(format, a...), 199 Property: property, 200 } 201} 202 203func extendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc, 204 order ExtendPropertyOrderFunc) error { 205 206 srcValue, err := getStruct(src) 207 if err != nil { 208 if _, ok := err.(getStructEmptyError); ok { 209 return nil 210 } 211 return err 212 } 213 214 dstValue, err := getOrCreateStruct(dst) 215 if err != nil { 216 return err 217 } 218 219 if dstValue.Type() != srcValue.Type() { 220 return fmt.Errorf("expected matching types for dst and src, got %T and %T", dst, src) 221 } 222 223 dstValues := []reflect.Value{dstValue} 224 225 return extendPropertiesRecursive(dstValues, srcValue, make([]string, 0, 8), filter, true, order) 226} 227 228func extendMatchingProperties(dst []interface{}, src interface{}, filter ExtendPropertyFilterFunc, 229 order ExtendPropertyOrderFunc) error { 230 231 srcValue, err := getStruct(src) 232 if err != nil { 233 if _, ok := err.(getStructEmptyError); ok { 234 return nil 235 } 236 return err 237 } 238 239 dstValues := make([]reflect.Value, len(dst)) 240 for i := range dst { 241 var err error 242 dstValues[i], err = getOrCreateStruct(dst[i]) 243 if err != nil { 244 return err 245 } 246 } 247 248 return extendPropertiesRecursive(dstValues, srcValue, make([]string, 0, 8), filter, false, order) 249} 250 251func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value, 252 prefix []string, filter ExtendPropertyFilterFunc, sameTypes bool, 253 orderFunc ExtendPropertyOrderFunc) error { 254 255 dstValuesCopied := false 256 257 propertyName := func(field reflect.StructField) string { 258 names := make([]string, 0, len(prefix)+1) 259 for _, s := range prefix { 260 names = append(names, PropertyNameForField(s)) 261 } 262 names = append(names, PropertyNameForField(field.Name)) 263 return strings.Join(names, ".") 264 } 265 266 srcType := srcValue.Type() 267 for i, srcField := range typeFields(srcType) { 268 if ShouldSkipProperty(srcField) { 269 continue 270 } 271 272 srcFieldValue := srcValue.Field(i) 273 274 // Step into source interfaces 275 if srcFieldValue.Kind() == reflect.Interface { 276 if srcFieldValue.IsNil() { 277 continue 278 } 279 280 srcFieldValue = srcFieldValue.Elem() 281 282 if srcFieldValue.Kind() != reflect.Ptr { 283 return extendPropertyErrorf(propertyName(srcField), "interface not a pointer") 284 } 285 } 286 287 // Step into source pointers to structs 288 if isStructPtr(srcFieldValue.Type()) { 289 if srcFieldValue.IsNil() { 290 continue 291 } 292 293 srcFieldValue = srcFieldValue.Elem() 294 } 295 296 found := false 297 var recurse []reflect.Value 298 // Use an iteration loop so elements can be added to the end of dstValues inside the loop. 299 for j := 0; j < len(dstValues); j++ { 300 dstValue := dstValues[j] 301 dstType := dstValue.Type() 302 var dstField reflect.StructField 303 304 dstFields := typeFields(dstType) 305 if dstType == srcType { 306 dstField = dstFields[i] 307 } else { 308 var ok bool 309 for _, field := range dstFields { 310 if field.Name == srcField.Name { 311 dstField = field 312 ok = true 313 } else if IsEmbedded(field) { 314 embeddedDstValue := dstValue.FieldByIndex(field.Index) 315 if isStructPtr(embeddedDstValue.Type()) { 316 if embeddedDstValue.IsNil() { 317 newEmbeddedDstValue := reflect.New(embeddedDstValue.Type().Elem()) 318 embeddedDstValue.Set(newEmbeddedDstValue) 319 } 320 embeddedDstValue = embeddedDstValue.Elem() 321 } 322 if !isStruct(embeddedDstValue.Type()) { 323 return extendPropertyErrorf(propertyName(srcField), "%s is not a struct (%s)", 324 propertyName(field), embeddedDstValue.Type()) 325 } 326 // The destination struct contains an embedded struct, add it to the list 327 // of destinations to consider. Make a copy of dstValues if necessary 328 // to avoid modifying the backing array of an input parameter. 329 if !dstValuesCopied { 330 dstValues = slices.Clone(dstValues) 331 dstValuesCopied = true 332 } 333 dstValues = append(dstValues, embeddedDstValue) 334 } 335 } 336 if !ok { 337 continue 338 } 339 } 340 341 found = true 342 343 dstFieldValue := dstValue.FieldByIndex(dstField.Index) 344 origDstFieldValue := dstFieldValue 345 346 // Step into destination interfaces 347 if dstFieldValue.Kind() == reflect.Interface { 348 if dstFieldValue.IsNil() { 349 return extendPropertyErrorf(propertyName(srcField), "nilitude mismatch") 350 } 351 352 dstFieldValue = dstFieldValue.Elem() 353 354 if dstFieldValue.Kind() != reflect.Ptr { 355 return extendPropertyErrorf(propertyName(srcField), "interface not a pointer") 356 } 357 } 358 359 // Step into destination pointers to structs 360 if isStructPtr(dstFieldValue.Type()) { 361 if dstFieldValue.IsNil() { 362 dstFieldValue = reflect.New(dstFieldValue.Type().Elem()) 363 origDstFieldValue.Set(dstFieldValue) 364 } 365 366 dstFieldValue = dstFieldValue.Elem() 367 } 368 369 switch srcFieldValue.Kind() { 370 case reflect.Struct: 371 if isConfigurable(srcField.Type) { 372 if srcFieldValue.Type() != dstFieldValue.Type() { 373 return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s", 374 dstFieldValue.Type(), srcFieldValue.Type()) 375 } 376 } else { 377 if sameTypes && dstFieldValue.Type() != srcFieldValue.Type() { 378 return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s", 379 dstFieldValue.Type(), srcFieldValue.Type()) 380 } 381 382 // Recursively extend the struct's fields. 383 recurse = append(recurse, dstFieldValue) 384 continue 385 } 386 case reflect.Bool, reflect.String, reflect.Slice, reflect.Map: 387 // If the types don't match or srcFieldValue cannot be converted to a Configurable type, it's an error 388 ct, err := configurableType(srcFieldValue.Type()) 389 if srcFieldValue.Type() != dstFieldValue.Type() && (err != nil || dstFieldValue.Type() != ct) { 390 return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s", 391 dstFieldValue.Type(), srcFieldValue.Type()) 392 } 393 case reflect.Ptr: 394 // If the types don't match or srcFieldValue cannot be converted to a Configurable type, it's an error 395 ct, err := configurableType(srcFieldValue.Type().Elem()) 396 if srcFieldValue.Type() != dstFieldValue.Type() && (err != nil || dstFieldValue.Type() != ct) { 397 return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s", 398 dstFieldValue.Type(), srcFieldValue.Type()) 399 } 400 switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind { 401 case reflect.Bool, reflect.Int64, reflect.String, reflect.Struct: 402 // Nothing 403 default: 404 return extendPropertyErrorf(propertyName(srcField), "pointer is a %s", ptrKind) 405 } 406 default: 407 return extendPropertyErrorf(propertyName(srcField), "unsupported kind %s", 408 srcFieldValue.Kind()) 409 } 410 411 if filter != nil { 412 b, err := filter(dstField, srcField) 413 if err != nil { 414 return &ExtendPropertyError{ 415 Property: propertyName(srcField), 416 Err: err, 417 } 418 } 419 if !b { 420 continue 421 } 422 } 423 424 order := Append 425 if orderFunc != nil { 426 var err error 427 order, err = orderFunc(dstField, srcField) 428 if err != nil { 429 return &ExtendPropertyError{ 430 Property: propertyName(srcField), 431 Err: err, 432 } 433 } 434 } 435 436 if HasTag(dstField, "android", "replace_instead_of_append") { 437 if order == Append { 438 order = Replace 439 } else if order == Prepend { 440 order = Prepend_replace 441 } 442 } 443 444 ExtendBasicType(dstFieldValue, srcFieldValue, order) 445 } 446 447 if len(recurse) > 0 { 448 err := extendPropertiesRecursive(recurse, srcFieldValue, 449 append(prefix, srcField.Name), filter, sameTypes, orderFunc) 450 if err != nil { 451 return err 452 } 453 } else if !found { 454 return extendPropertyErrorf(propertyName(srcField), "failed to find property to extend") 455 } 456 } 457 458 return nil 459} 460 461func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) { 462 prepend := order == Prepend || order == Prepend_replace 463 464 if !srcFieldValue.IsValid() { 465 return 466 } 467 468 // If dst is a Configurable and src isn't, promote src to a Configurable. 469 // This isn't necessary if all property structs are using Configurable values, 470 // but it's helpful to avoid having to change as many places in the code when 471 // converting properties to Configurable properties. For example, load hooks 472 // make their own mini-property structs and append them onto the main property 473 // structs when they want to change the default values of properties. 474 srcFieldType := srcFieldValue.Type() 475 if isConfigurable(dstFieldValue.Type()) && !isConfigurable(srcFieldType) { 476 var value reflect.Value 477 if srcFieldType.Kind() == reflect.Pointer { 478 srcFieldType = srcFieldType.Elem() 479 if srcFieldValue.IsNil() { 480 value = srcFieldValue 481 } else { 482 // Copy the pointer 483 value = reflect.New(srcFieldType) 484 value.Elem().Set(srcFieldValue.Elem()) 485 } 486 } else { 487 value = reflect.New(srcFieldType) 488 value.Elem().Set(srcFieldValue) 489 } 490 caseType := configurableCaseType(srcFieldType) 491 case_ := reflect.New(caseType) 492 case_.Interface().(configurableCaseReflection).initialize(nil, value.Interface()) 493 cases := reflect.MakeSlice(reflect.SliceOf(caseType), 0, 1) 494 cases = reflect.Append(cases, case_.Elem()) 495 ct, err := configurableType(srcFieldType) 496 if err != nil { 497 // Should be unreachable due to earlier checks 498 panic(err.Error()) 499 } 500 temp := reflect.New(ct) 501 temp.Interface().(configurablePtrReflection).initialize("", nil, cases.Interface()) 502 srcFieldValue = temp.Elem() 503 } 504 505 switch srcFieldValue.Kind() { 506 case reflect.Struct: 507 if !isConfigurable(srcFieldValue.Type()) { 508 panic("Should be unreachable") 509 } 510 replace := order == Prepend_replace || order == Replace 511 unpackedDst := dstFieldValue.Interface().(configurableReflection) 512 if unpackedDst.isEmpty() { 513 // Properties that were never initialized via unpacking from a bp file value 514 // will have a nil inner value, making them unable to be modified without a pointer 515 // like we don't have here. So instead replace the whole configurable object. 516 dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Interface().(configurableReflection).clone())) 517 } else { 518 unpackedDst.setAppend(srcFieldValue.Interface(), replace, prepend) 519 } 520 case reflect.Bool: 521 // Boolean OR 522 dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Bool() || dstFieldValue.Bool())) 523 case reflect.String: 524 if prepend { 525 dstFieldValue.SetString(srcFieldValue.String() + 526 dstFieldValue.String()) 527 } else { 528 dstFieldValue.SetString(dstFieldValue.String() + 529 srcFieldValue.String()) 530 } 531 case reflect.Slice: 532 if srcFieldValue.IsNil() { 533 break 534 } 535 536 newSlice := reflect.MakeSlice(srcFieldValue.Type(), 0, 537 dstFieldValue.Len()+srcFieldValue.Len()) 538 if prepend { 539 newSlice = reflect.AppendSlice(newSlice, srcFieldValue) 540 newSlice = reflect.AppendSlice(newSlice, dstFieldValue) 541 } else if order == Append { 542 newSlice = reflect.AppendSlice(newSlice, dstFieldValue) 543 newSlice = reflect.AppendSlice(newSlice, srcFieldValue) 544 } else { 545 // replace 546 newSlice = reflect.AppendSlice(newSlice, srcFieldValue) 547 } 548 dstFieldValue.Set(newSlice) 549 case reflect.Map: 550 if srcFieldValue.IsNil() { 551 break 552 } 553 var mapValue reflect.Value 554 // for append/prepend, maintain keys from original value 555 // for replace, replace entire map 556 if order == Replace || dstFieldValue.IsNil() { 557 mapValue = srcFieldValue 558 } else { 559 mapValue = dstFieldValue 560 561 iter := srcFieldValue.MapRange() 562 for iter.Next() { 563 dstValue := dstFieldValue.MapIndex(iter.Key()) 564 if prepend { 565 // if the key exists in the map, keep the original value. 566 if !dstValue.IsValid() { 567 // otherwise, add the new value 568 mapValue.SetMapIndex(iter.Key(), iter.Value()) 569 } 570 } else { 571 // For append, replace the original value. 572 mapValue.SetMapIndex(iter.Key(), iter.Value()) 573 } 574 } 575 } 576 dstFieldValue.Set(mapValue) 577 case reflect.Ptr: 578 if srcFieldValue.IsNil() { 579 break 580 } 581 582 switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind { 583 case reflect.Bool: 584 if prepend { 585 if dstFieldValue.IsNil() { 586 dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool()))) 587 } 588 } else { 589 // For append, replace the original value. 590 dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool()))) 591 } 592 case reflect.Int64: 593 if prepend { 594 if dstFieldValue.IsNil() { 595 // Int() returns Int64 596 dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int()))) 597 } 598 } else { 599 // For append, replace the original value. 600 // Int() returns Int64 601 dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int()))) 602 } 603 case reflect.String: 604 if prepend { 605 if dstFieldValue.IsNil() { 606 dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String()))) 607 } 608 } else { 609 // For append, replace the original value. 610 dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String()))) 611 } 612 case reflect.Struct: 613 srcFieldValue := srcFieldValue.Elem() 614 if !isConfigurable(srcFieldValue.Type()) { 615 panic("Should be unreachable") 616 } 617 panic("Don't use pointers to Configurable properties. All Configurable properties can be unset, " + 618 "and the 'replacing' behavior can be accomplished with the `blueprint:\"replace_instead_of_append\" " + 619 "struct field tag. There's no reason to have a pointer configurable property.") 620 default: 621 panic(fmt.Errorf("unexpected pointer kind %s", ptrKind)) 622 } 623 } 624} 625 626// ShouldSkipProperty indicates whether a property should be skipped in processing. 627func ShouldSkipProperty(structField reflect.StructField) bool { 628 return structField.PkgPath != "" || // The field is not exported so just skip it. 629 HasTag(structField, "blueprint", "mutated") // The field is not settable in a blueprint file 630} 631 632// IsEmbedded indicates whether a property is embedded. This is useful for determining nesting name 633// as the name of the embedded field is _not_ used in blueprint files. 634func IsEmbedded(structField reflect.StructField) bool { 635 return structField.Name == "BlueprintEmbed" || structField.Anonymous 636} 637 638type getStructEmptyError struct{} 639 640func (getStructEmptyError) Error() string { return "interface containing nil pointer" } 641 642func getOrCreateStruct(in interface{}) (reflect.Value, error) { 643 value, err := getStruct(in) 644 if _, ok := err.(getStructEmptyError); ok { 645 value := reflect.ValueOf(in) 646 newValue := reflect.New(value.Type().Elem()) 647 value.Set(newValue) 648 } 649 650 return value, err 651} 652 653func getStruct(in interface{}) (reflect.Value, error) { 654 value := reflect.ValueOf(in) 655 if !isStructPtr(value.Type()) { 656 return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %s", value.Type()) 657 } 658 if value.IsNil() { 659 return reflect.Value{}, getStructEmptyError{} 660 } 661 value = value.Elem() 662 return value, nil 663} 664