blob: d3c2b799b15b2db221ae1f8a3f61229a6d48094f [file] [log] [blame]
Colin Cross0bc7e072015-10-27 18:15:15 -07001// 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)
21
22// AppendProperties appends the values of properties in the property struct src to the property
23// struct dst. dst and src must be the same type, and both must be pointers to structs.
24//
25// The filter function can prevent individual properties from being appended by returning false, or
26// abort AppendProperties with an error by returning an error. Passing nil for filter will append
27// all properties.
28//
29// An error returned by AppendProperties that applies to a specific property will be an
30// *ExtendPropertyError, and can have the property name and error extracted from it.
31//
Dan Willemsen4c000852016-01-05 14:16:04 -080032// The append operation is defined as appending strings and slices of strings normally, OR-ing bool
33// values, replacing non-nil pointers to booleans or strings, and recursing into
Colin Cross80117682015-10-30 15:53:55 -070034// embedded structs, pointers to structs, and interfaces containing
Colin Cross0bc7e072015-10-27 18:15:15 -070035// pointers to structs. Appending the zero value of a property will always be a no-op.
36func AppendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error {
Jaewoong Jung4764fa72019-06-22 11:19:34 -070037 return extendProperties(dst, src, filter, OrderAppend)
Colin Cross0bc7e072015-10-27 18:15:15 -070038}
39
40// PrependProperties prepends the values of properties in the property struct src to the property
41// struct dst. dst and src must be the same type, and both must be pointers to structs.
42//
43// The filter function can prevent individual properties from being prepended by returning false, or
44// abort PrependProperties with an error by returning an error. Passing nil for filter will prepend
45// all properties.
46//
47// An error returned by PrependProperties that applies to a specific property will be an
48// *ExtendPropertyError, and can have the property name and error extracted from it.
49//
Dan Willemsen4c000852016-01-05 14:16:04 -080050// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing
51// bool values, replacing non-nil pointers to booleans or strings, and recursing into
Colin Cross80117682015-10-30 15:53:55 -070052// embedded structs, pointers to structs, and interfaces containing
Colin Cross0bc7e072015-10-27 18:15:15 -070053// pointers to structs. Prepending the zero value of a property will always be a no-op.
54func PrependProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error {
Jaewoong Jung4764fa72019-06-22 11:19:34 -070055 return extendProperties(dst, src, filter, OrderPrepend)
Colin Cross0bc7e072015-10-27 18:15:15 -070056}
57
58// AppendMatchingProperties appends the values of properties in the property struct src to the
59// property structs in dst. dst and src do not have to be the same type, but every property in src
60// must be found in at least one property in dst. dst must be a slice of pointers to structs, and
61// src must be a pointer to a struct.
62//
63// The filter function can prevent individual properties from being appended by returning false, or
64// abort AppendProperties with an error by returning an error. Passing nil for filter will append
65// all properties.
66//
67// An error returned by AppendMatchingProperties that applies to a specific property will be an
68// *ExtendPropertyError, and can have the property name and error extracted from it.
69//
Dan Willemsen4c000852016-01-05 14:16:04 -080070// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool
Jaewoong Jungc0672512019-07-08 13:40:28 -070071// values, replacing pointers to booleans or strings whether they are nil or not, and recursing into
Colin Cross80117682015-10-30 15:53:55 -070072// embedded structs, pointers to structs, and interfaces containing
Colin Cross0bc7e072015-10-27 18:15:15 -070073// pointers to structs. Appending the zero value of a property will always be a no-op.
74func AppendMatchingProperties(dst []interface{}, src interface{},
75 filter ExtendPropertyFilterFunc) error {
Jaewoong Jung4764fa72019-06-22 11:19:34 -070076 return extendMatchingProperties(dst, src, filter, OrderAppend)
Colin Cross0bc7e072015-10-27 18:15:15 -070077}
78
79// PrependMatchingProperties prepends the values of properties in the property struct src to the
80// property structs in dst. dst and src do not have to be the same type, but every property in src
81// must be found in at least one property in dst. dst must be a slice of pointers to structs, and
82// src must be a pointer to a struct.
83//
84// The filter function can prevent individual properties from being prepended by returning false, or
85// abort PrependProperties with an error by returning an error. Passing nil for filter will prepend
86// all properties.
87//
88// An error returned by PrependProperties that applies to a specific property will be an
89// *ExtendPropertyError, and can have the property name and error extracted from it.
90//
Dan Willemsen4c000852016-01-05 14:16:04 -080091// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing
Jaewoong Jungc0672512019-07-08 13:40:28 -070092// bool values, replacing nil pointers to booleans or strings, and recursing into
Colin Cross80117682015-10-30 15:53:55 -070093// embedded structs, pointers to structs, and interfaces containing
Colin Cross0bc7e072015-10-27 18:15:15 -070094// pointers to structs. Prepending the zero value of a property will always be a no-op.
95func PrependMatchingProperties(dst []interface{}, src interface{},
96 filter ExtendPropertyFilterFunc) error {
Jaewoong Jung4764fa72019-06-22 11:19:34 -070097 return extendMatchingProperties(dst, src, filter, OrderPrepend)
Colin Cross0bc7e072015-10-27 18:15:15 -070098}
99
Colin Cross75c47012016-05-05 15:58:02 -0700100// ExtendProperties appends or prepends the values of properties in the property struct src to the
101// property struct dst. dst and src must be the same type, and both must be pointers to structs.
102//
103// The filter function can prevent individual properties from being appended or prepended by
104// returning false, or abort ExtendProperties with an error by returning an error. Passing nil for
105// filter will append or prepend all properties.
106//
107// The order function is called on each non-filtered property to determine if it should be appended
108// or prepended.
109//
110// An error returned by ExtendProperties that applies to a specific property will be an
111// *ExtendPropertyError, and can have the property name and error extracted from it.
112//
113// The append operation is defined as appending strings and slices of strings normally, OR-ing bool
114// values, replacing non-nil pointers to booleans or strings, and recursing into
115// embedded structs, pointers to structs, and interfaces containing
116// pointers to structs. Appending or prepending the zero value of a property will always be a
117// no-op.
118func ExtendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc,
119 order ExtendPropertyOrderFunc) error {
120 return extendProperties(dst, src, filter, order)
121}
122
123// ExtendMatchingProperties appends or prepends the values of properties in the property struct src
124// to the property structs in dst. dst and src do not have to be the same type, but every property
125// in src must be found in at least one property in dst. dst must be a slice of pointers to
126// structs, and src must be a pointer to a struct.
127//
128// The filter function can prevent individual properties from being appended or prepended by
129// returning false, or abort ExtendMatchingProperties with an error by returning an error. Passing
130// nil for filter will append or prepend all properties.
131//
132// The order function is called on each non-filtered property to determine if it should be appended
133// or prepended.
134//
135// An error returned by ExtendMatchingProperties that applies to a specific property will be an
136// *ExtendPropertyError, and can have the property name and error extracted from it.
137//
138// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool
139// values, replacing non-nil pointers to booleans or strings, and recursing into
140// embedded structs, pointers to structs, and interfaces containing
141// pointers to structs. Appending or prepending the zero value of a property will always be a
142// no-op.
143func ExtendMatchingProperties(dst []interface{}, src interface{},
144 filter ExtendPropertyFilterFunc, order ExtendPropertyOrderFunc) error {
145 return extendMatchingProperties(dst, src, filter, order)
146}
147
148type Order int
149
150const (
151 Append Order = iota
152 Prepend
Jiyong Park10f27f82019-11-15 18:31:56 +0900153 Replace
Colin Cross75c47012016-05-05 15:58:02 -0700154)
155
Colin Cross0bc7e072015-10-27 18:15:15 -0700156type ExtendPropertyFilterFunc func(property string,
157 dstField, srcField reflect.StructField,
158 dstValue, srcValue interface{}) (bool, error)
159
Colin Cross75c47012016-05-05 15:58:02 -0700160type ExtendPropertyOrderFunc func(property string,
161 dstField, srcField reflect.StructField,
162 dstValue, srcValue interface{}) (Order, error)
163
Jaewoong Jung4764fa72019-06-22 11:19:34 -0700164func OrderAppend(property string,
Colin Cross75c47012016-05-05 15:58:02 -0700165 dstField, srcField reflect.StructField,
166 dstValue, srcValue interface{}) (Order, error) {
167 return Append, nil
168}
169
Jaewoong Jung4764fa72019-06-22 11:19:34 -0700170func OrderPrepend(property string,
Colin Cross75c47012016-05-05 15:58:02 -0700171 dstField, srcField reflect.StructField,
172 dstValue, srcValue interface{}) (Order, error) {
173 return Prepend, nil
174}
175
Jiyong Park10f27f82019-11-15 18:31:56 +0900176func OrderReplace(property string,
177 dstField, srcField reflect.StructField,
178 dstValue, srcValue interface{}) (Order, error) {
179 return Replace, nil
180}
181
Colin Cross0bc7e072015-10-27 18:15:15 -0700182type ExtendPropertyError struct {
183 Err error
184 Property string
185}
186
187func (e *ExtendPropertyError) Error() string {
188 return fmt.Sprintf("can't extend property %q: %s", e.Property, e.Err)
189}
190
191func extendPropertyErrorf(property string, format string, a ...interface{}) *ExtendPropertyError {
192 return &ExtendPropertyError{
193 Err: fmt.Errorf(format, a...),
194 Property: property,
195 }
196}
197
198func extendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc,
Colin Cross75c47012016-05-05 15:58:02 -0700199 order ExtendPropertyOrderFunc) error {
Colin Cross0bc7e072015-10-27 18:15:15 -0700200
Colin Crossc3d73122016-08-05 17:19:36 -0700201 srcValue, err := getStruct(src)
Colin Cross0bc7e072015-10-27 18:15:15 -0700202 if err != nil {
Colin Crossc3d73122016-08-05 17:19:36 -0700203 if _, ok := err.(getStructEmptyError); ok {
204 return nil
205 }
Colin Cross0bc7e072015-10-27 18:15:15 -0700206 return err
207 }
Colin Crossc3d73122016-08-05 17:19:36 -0700208
209 dstValue, err := getOrCreateStruct(dst)
Colin Cross0bc7e072015-10-27 18:15:15 -0700210 if err != nil {
211 return err
212 }
213
214 if dstValue.Type() != srcValue.Type() {
215 return fmt.Errorf("expected matching types for dst and src, got %T and %T", dst, src)
216 }
217
218 dstValues := []reflect.Value{dstValue}
219
Colin Cross75c47012016-05-05 15:58:02 -0700220 return extendPropertiesRecursive(dstValues, srcValue, "", filter, true, order)
Colin Cross0bc7e072015-10-27 18:15:15 -0700221}
222
223func extendMatchingProperties(dst []interface{}, src interface{}, filter ExtendPropertyFilterFunc,
Colin Cross75c47012016-05-05 15:58:02 -0700224 order ExtendPropertyOrderFunc) error {
Colin Cross0bc7e072015-10-27 18:15:15 -0700225
Colin Crossc3d73122016-08-05 17:19:36 -0700226 srcValue, err := getStruct(src)
227 if err != nil {
228 if _, ok := err.(getStructEmptyError); ok {
229 return nil
230 }
231 return err
232 }
233
Colin Cross0bc7e072015-10-27 18:15:15 -0700234 dstValues := make([]reflect.Value, len(dst))
235 for i := range dst {
236 var err error
Colin Crossc3d73122016-08-05 17:19:36 -0700237 dstValues[i], err = getOrCreateStruct(dst[i])
Colin Cross0bc7e072015-10-27 18:15:15 -0700238 if err != nil {
239 return err
240 }
241 }
242
Colin Cross75c47012016-05-05 15:58:02 -0700243 return extendPropertiesRecursive(dstValues, srcValue, "", filter, false, order)
Colin Cross0bc7e072015-10-27 18:15:15 -0700244}
245
246func extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value,
Colin Cross75c47012016-05-05 15:58:02 -0700247 prefix string, filter ExtendPropertyFilterFunc, sameTypes bool,
Colin Cross05b36072017-07-28 17:51:37 -0700248 orderFunc ExtendPropertyOrderFunc) error {
Colin Cross0bc7e072015-10-27 18:15:15 -0700249
250 srcType := srcValue.Type()
Colin Cross01ee36e2016-05-17 13:57:12 -0700251 for i, srcField := range typeFields(srcType) {
Colin Cross0bc7e072015-10-27 18:15:15 -0700252 if srcField.PkgPath != "" {
253 // The field is not exported so just skip it.
254 continue
255 }
256 if HasTag(srcField, "blueprint", "mutated") {
257 continue
258 }
259
260 propertyName := prefix + PropertyNameForField(srcField.Name)
261 srcFieldValue := srcValue.Field(i)
262
Colin Crossbf2adbf2016-08-19 18:16:33 -0700263 // Step into source interfaces
264 if srcFieldValue.Kind() == reflect.Interface {
265 if srcFieldValue.IsNil() {
266 continue
267 }
268
269 srcFieldValue = srcFieldValue.Elem()
270
271 if srcFieldValue.Kind() != reflect.Ptr {
272 return extendPropertyErrorf(propertyName, "interface not a pointer")
273 }
274 }
275
276 // Step into source pointers to structs
Colin Cross6898d262020-01-27 16:48:30 -0800277 if isStructPtr(srcFieldValue.Type()) {
Colin Crossbf2adbf2016-08-19 18:16:33 -0700278 if srcFieldValue.IsNil() {
279 continue
280 }
281
282 srcFieldValue = srcFieldValue.Elem()
283 }
284
Colin Cross0bc7e072015-10-27 18:15:15 -0700285 found := false
Colin Crossbf2adbf2016-08-19 18:16:33 -0700286 var recurse []reflect.Value
Colin Cross0bc7e072015-10-27 18:15:15 -0700287 for _, dstValue := range dstValues {
288 dstType := dstValue.Type()
289 var dstField reflect.StructField
290
Colin Cross01ee36e2016-05-17 13:57:12 -0700291 dstFields := typeFields(dstType)
Colin Cross0bc7e072015-10-27 18:15:15 -0700292 if dstType == srcType {
Colin Cross01ee36e2016-05-17 13:57:12 -0700293 dstField = dstFields[i]
Colin Cross0bc7e072015-10-27 18:15:15 -0700294 } else {
295 var ok bool
Colin Cross01ee36e2016-05-17 13:57:12 -0700296 for _, field := range dstFields {
297 if field.Name == srcField.Name {
298 dstField = field
299 ok = true
300 }
301 }
Colin Cross0bc7e072015-10-27 18:15:15 -0700302 if !ok {
303 continue
304 }
305 }
306
307 found = true
308
309 dstFieldValue := dstValue.FieldByIndex(dstField.Index)
Colin Crossc3d73122016-08-05 17:19:36 -0700310 origDstFieldValue := dstFieldValue
Colin Cross0bc7e072015-10-27 18:15:15 -0700311
Colin Crossbf2adbf2016-08-19 18:16:33 -0700312 // Step into destination interfaces
313 if dstFieldValue.Kind() == reflect.Interface {
Colin Cross0bc7e072015-10-27 18:15:15 -0700314 if dstFieldValue.IsNil() {
Colin Crossbf2adbf2016-08-19 18:16:33 -0700315 return extendPropertyErrorf(propertyName, "nilitude mismatch")
Colin Cross0bc7e072015-10-27 18:15:15 -0700316 }
317
318 dstFieldValue = dstFieldValue.Elem()
Colin Cross0bc7e072015-10-27 18:15:15 -0700319
Colin Crossbf2adbf2016-08-19 18:16:33 -0700320 if dstFieldValue.Kind() != reflect.Ptr {
Colin Cross0bc7e072015-10-27 18:15:15 -0700321 return extendPropertyErrorf(propertyName, "interface not a pointer")
322 }
Colin Crossbf2adbf2016-08-19 18:16:33 -0700323 }
Colin Cross0bc7e072015-10-27 18:15:15 -0700324
Colin Crossbf2adbf2016-08-19 18:16:33 -0700325 // Step into destination pointers to structs
Colin Cross6898d262020-01-27 16:48:30 -0800326 if isStructPtr(dstFieldValue.Type()) {
Colin Cross0bc7e072015-10-27 18:15:15 -0700327 if dstFieldValue.IsNil() {
Colin Crossbf2adbf2016-08-19 18:16:33 -0700328 dstFieldValue = reflect.New(dstFieldValue.Type().Elem())
Colin Crossc3d73122016-08-05 17:19:36 -0700329 origDstFieldValue.Set(dstFieldValue)
Colin Cross0bc7e072015-10-27 18:15:15 -0700330 }
331
332 dstFieldValue = dstFieldValue.Elem()
Colin Crossbf2adbf2016-08-19 18:16:33 -0700333 }
Colin Cross0bc7e072015-10-27 18:15:15 -0700334
Colin Crossbf2adbf2016-08-19 18:16:33 -0700335 switch srcFieldValue.Kind() {
Colin Cross0bc7e072015-10-27 18:15:15 -0700336 case reflect.Struct:
337 if sameTypes && dstFieldValue.Type() != srcFieldValue.Type() {
338 return extendPropertyErrorf(propertyName, "mismatched types %s and %s",
339 dstFieldValue.Type(), srcFieldValue.Type())
340 }
341
342 // Recursively extend the struct's fields.
Colin Crossbf2adbf2016-08-19 18:16:33 -0700343 recurse = append(recurse, dstFieldValue)
Colin Cross0bc7e072015-10-27 18:15:15 -0700344 continue
345 case reflect.Bool, reflect.String, reflect.Slice:
346 if srcFieldValue.Type() != dstFieldValue.Type() {
347 return extendPropertyErrorf(propertyName, "mismatched types %s and %s",
348 dstFieldValue.Type(), srcFieldValue.Type())
349 }
Colin Crossbf2adbf2016-08-19 18:16:33 -0700350 case reflect.Ptr:
351 if srcFieldValue.Type() != dstFieldValue.Type() {
352 return extendPropertyErrorf(propertyName, "mismatched types %s and %s",
353 dstFieldValue.Type(), srcFieldValue.Type())
354 }
355 switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind {
Nan Zhangf5865442017-11-01 14:03:28 -0700356 case reflect.Bool, reflect.Int64, reflect.String, reflect.Struct:
Colin Crossbf2adbf2016-08-19 18:16:33 -0700357 // Nothing
358 default:
359 return extendPropertyErrorf(propertyName, "pointer is a %s", ptrKind)
360 }
Colin Cross0bc7e072015-10-27 18:15:15 -0700361 default:
362 return extendPropertyErrorf(propertyName, "unsupported kind %s",
363 srcFieldValue.Kind())
364 }
365
Colin Cross01ee36e2016-05-17 13:57:12 -0700366 dstFieldInterface := dstFieldValue.Interface()
367 srcFieldInterface := srcFieldValue.Interface()
368
Colin Cross0bc7e072015-10-27 18:15:15 -0700369 if filter != nil {
370 b, err := filter(propertyName, dstField, srcField,
Colin Cross01ee36e2016-05-17 13:57:12 -0700371 dstFieldInterface, srcFieldInterface)
Colin Cross0bc7e072015-10-27 18:15:15 -0700372 if err != nil {
373 return &ExtendPropertyError{
374 Property: propertyName,
375 Err: err,
376 }
377 }
378 if !b {
379 continue
380 }
381 }
382
Colin Cross05b36072017-07-28 17:51:37 -0700383 order := Append
384 if orderFunc != nil {
385 var err error
386 order, err = orderFunc(propertyName, dstField, srcField,
Colin Cross01ee36e2016-05-17 13:57:12 -0700387 dstFieldInterface, srcFieldInterface)
Colin Cross75c47012016-05-05 15:58:02 -0700388 if err != nil {
389 return &ExtendPropertyError{
390 Property: propertyName,
391 Err: err,
392 }
393 }
Colin Cross75c47012016-05-05 15:58:02 -0700394 }
395
Colin Cross05b36072017-07-28 17:51:37 -0700396 ExtendBasicType(dstFieldValue, srcFieldValue, order)
Colin Cross0bc7e072015-10-27 18:15:15 -0700397 }
Colin Cross05b36072017-07-28 17:51:37 -0700398
Colin Crossbf2adbf2016-08-19 18:16:33 -0700399 if len(recurse) > 0 {
400 err := extendPropertiesRecursive(recurse, srcFieldValue,
Colin Cross05b36072017-07-28 17:51:37 -0700401 propertyName+".", filter, sameTypes, orderFunc)
Colin Crossbf2adbf2016-08-19 18:16:33 -0700402 if err != nil {
403 return err
404 }
405 } else if !found {
Colin Cross0bc7e072015-10-27 18:15:15 -0700406 return extendPropertyErrorf(propertyName, "failed to find property to extend")
407 }
408 }
409
410 return nil
411}
412
Colin Cross05b36072017-07-28 17:51:37 -0700413func ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) {
414 prepend := order == Prepend
415
416 switch srcFieldValue.Kind() {
417 case reflect.Bool:
418 // Boolean OR
419 dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Bool() || dstFieldValue.Bool()))
420 case reflect.String:
421 if prepend {
422 dstFieldValue.SetString(srcFieldValue.String() +
423 dstFieldValue.String())
424 } else {
425 dstFieldValue.SetString(dstFieldValue.String() +
426 srcFieldValue.String())
427 }
428 case reflect.Slice:
429 if srcFieldValue.IsNil() {
430 break
431 }
432
433 newSlice := reflect.MakeSlice(srcFieldValue.Type(), 0,
434 dstFieldValue.Len()+srcFieldValue.Len())
435 if prepend {
436 newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
437 newSlice = reflect.AppendSlice(newSlice, dstFieldValue)
Jiyong Park10f27f82019-11-15 18:31:56 +0900438 } else if order == Append {
Colin Cross05b36072017-07-28 17:51:37 -0700439 newSlice = reflect.AppendSlice(newSlice, dstFieldValue)
440 newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
Jiyong Park10f27f82019-11-15 18:31:56 +0900441 } else {
442 // replace
443 newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
Colin Cross05b36072017-07-28 17:51:37 -0700444 }
445 dstFieldValue.Set(newSlice)
446 case reflect.Ptr:
447 if srcFieldValue.IsNil() {
448 break
449 }
450
451 switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind {
452 case reflect.Bool:
453 if prepend {
454 if dstFieldValue.IsNil() {
455 dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool())))
456 }
457 } else {
458 // For append, replace the original value.
459 dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool())))
460 }
Nan Zhangf5865442017-11-01 14:03:28 -0700461 case reflect.Int64:
462 if prepend {
463 if dstFieldValue.IsNil() {
464 // Int() returns Int64
465 dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int())))
466 }
467 } else {
468 // For append, replace the original value.
469 // Int() returns Int64
470 dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int())))
471 }
Colin Cross05b36072017-07-28 17:51:37 -0700472 case reflect.String:
473 if prepend {
474 if dstFieldValue.IsNil() {
475 dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String())))
476 }
477 } else {
478 // For append, replace the original value.
479 dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String())))
480 }
481 default:
482 panic(fmt.Errorf("unexpected pointer kind %s", ptrKind))
483 }
484 }
485}
486
Colin Crossc3d73122016-08-05 17:19:36 -0700487type getStructEmptyError struct{}
488
489func (getStructEmptyError) Error() string { return "interface containing nil pointer" }
490
491func getOrCreateStruct(in interface{}) (reflect.Value, error) {
492 value, err := getStruct(in)
493 if _, ok := err.(getStructEmptyError); ok {
494 value := reflect.ValueOf(in)
495 newValue := reflect.New(value.Type().Elem())
496 value.Set(newValue)
497 }
498
499 return value, err
500}
501
Colin Cross0bc7e072015-10-27 18:15:15 -0700502func getStruct(in interface{}) (reflect.Value, error) {
503 value := reflect.ValueOf(in)
Colin Cross6898d262020-01-27 16:48:30 -0800504 if !isStructPtr(value.Type()) {
505 return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %s", value.Type())
Colin Cross0bc7e072015-10-27 18:15:15 -0700506 }
Colin Crossc3d73122016-08-05 17:19:36 -0700507 if value.IsNil() {
508 return reflect.Value{}, getStructEmptyError{}
509 }
510 value = value.Elem()
Colin Cross0bc7e072015-10-27 18:15:15 -0700511 return value, nil
512}