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