func mergeStructs(dstStruct, srcStruct interface{}, fpm FieldPresenceMap) {
	dst := reflect.ValueOf(dstStruct)
	src := reflect.ValueOf(srcStruct)
	typ := src.Type()

	// Dereference pointers
	if src.Kind() == reflect.Ptr {
		src = src.Elem()
		typ = src.Type()
	}
	if dst.Kind() == reflect.Ptr {
		dst = dst.Elem()
	}
	dstTyp := dst.Type()

	// Sanity check
	if typ.Kind() != reflect.Struct || typ != dstTyp {
		panic(errors.Errorf("BUG: source/destination type mismatch; expected destination to be a %q; got %q", typ.Name(), dstTyp.Name()))
	}

	for i := 0; i < typ.NumField(); i++ {
		// Skip non-exportable fields
		structField := typ.Field(i)
		srcVal := src.Field(i)
		if !srcVal.CanInterface() {
			continue
		}

		fieldName := yamlName(structField)
		if !fpm.fieldPresent(fieldName) {
			continue
		}

		switch srcVal.Kind() {
		case reflect.Map:
			// If a nil/empty map is provided then clear the destination map.
			if isZero(srcVal) {
				dst.Field(i).Set(reflect.MakeMap(srcVal.Type()))
				continue
			}

			dstMap := dst.Field(i)
			if dstMap.IsNil() {
				dstMap.Set(reflect.MakeMap(srcVal.Type()))
			}
			for _, srcKey := range srcVal.MapKeys() {
				// If the key points to an empty non-scalar value delete it from the dst map
				srcMapVal := srcVal.MapIndex(srcKey)
				if isZero(srcMapVal) && isNonScalar(srcMapVal) {
					// Setting an empty value effectively deletes the key from the map
					dstMap.SetMapIndex(srcKey, reflect.Value{})
					continue
				}

				dstMap.SetMapIndex(srcKey, srcMapVal)
			}
		case reflect.Slice:
			dst.Field(i).Set(srcVal)
		default:
			dst.Field(i).Set(srcVal)
		}
	}
}
// mergeStructs iterates the fields of srcStruct and merges them into the
// equivalent fields of dstStruct using the following rules:
//
// - if src defines a value for a scalar or slice field, it will overwrite
//   the value from the dst (e.g. trust, series etc).
// - if the src specifies a nil/empty value for a map field, then the map
//   field of dst will be cleared.
// - if the src specifies a non-empty value for a map field, its key/value
//   tuples are iterated and:
//   - if the value is nil/zero and non-scalar, it is deleted from the dst map.
//   - otherwise, the key/value is inserted into the dst map overwriting any
//     existing entries.
mergeStructs iterates the fields of srcStruct and merges them into the
equivalent fields of dstStruct using the following rules.

- if src defines a value for a scalar or slice field, it will overwrite
the value from the dst .
- if the src specifies a nil/empty value for a map field, then the map
field of dst will be cleared.
- if the src specifies a non-empty value for a map field, its key/value
tuples are iterated and:
- if the value is nil/zero and non-scalar, it is deleted from the dst map.
- otherwise, the key/value is inserted into the dst map overwriting any
existing entries.
// mergeStructs iterates the fields of srcStruct and merges them into the // equivalent fields of dstStruct using the following rules : // // - if src defines a value for a scalar or slice field it will overwrite // the value from the dst ( e . g . trust series etc ) . // - if the src specifies a nil / empty value for a map field then the map // field of dst will be cleared . // - if the src specifies a non - empty value for a map field its key / value // tuples are iterated and : // - if the value is nil / zero and non - scalar it is deleted from the dst map . // - otherwise the key / value is inserted into the dst map overwriting any // existing entries .
func mergeStructs(dstStruct, srcStruct interface{}, fpm FieldPresenceMap) {
	dst := reflect.ValueOf(dstStruct)
	src := reflect.ValueOf(srcStruct)
	typ := src.Type()
	if src.Kind() == reflect.Ptr {
		src = src.Elem()
		typ = src.Type()
	}
	if dst.Kind() == reflect.Ptr {
		dst = dst.Elem()
	}
	dstTyp := dst.Type()
	if typ.Kind() != reflect.Struct || typ != dstTyp {
		panic(errors.Errorf("BUG: source/destination type mismatch; expected destination to be a %q; got %q", typ.Name(), dstTyp.Name()))
	}
	for i := 0; i < typ.NumField(); i++ {
		structField := typ.Field(i)
		srcVal := src.Field(i)
		if !srcVal.CanInterface() {
			continue
		}
		fieldName := yamlName(structField)
		if !fpm.fieldPresent(fieldName) {
			continue
		}
		switch srcVal.Kind() {
		case reflect.Map:
			if isZero(srcVal) {
				dst.Field(i).Set(reflect.MakeMap(srcVal.Type()))
				continue
			}
			dstMap := dst.Field(i)
			if dstMap.IsNil() {
				dstMap.Set(reflect.MakeMap(srcVal.Type()))
			}
			for _, srcKey := range srcVal.MapKeys() {
				srcMapVal := srcVal.MapIndex(srcKey)
				if isZero(srcMapVal) && isNonScalar(srcMapVal) {
					dstMap.SetMapIndex(srcKey, reflect.Value{})
					continue
				}
				dstMap.SetMapIndex(srcKey, srcMapVal)
			}
		case reflect.Slice:
			dst.Field(i).Set(srcVal)
		default:
			dst.Field(i).Set(srcVal)
		}
	}
}
func mergeStructs ( dstStruct , srcStruct interface { } , fpm FieldPresenceMap ) { dst := reflect . ValueOf ( dstStruct ) src := reflect . ValueOf ( srcStruct ) typ := src . Type ( ) if src . Kind ( ) == reflect . Ptr { src = src . Elem ( ) typ = src . Type ( ) } if dst . Kind ( ) == reflect . Ptr { dst = dst . Elem ( ) } dstTyp := dst . Type ( ) if typ . Kind ( ) != reflect . Struct || typ != dstTyp { panic ( errors . Errorf ( " " , typ . Name ( ) , dstTyp . Name ( ) ) ) } for i := 0 ; i < typ . NumField ( ) ; i ++ { structField := typ . Field ( i ) srcVal := src . Field ( i ) if ! srcVal . CanInterface ( ) { continue } fieldName := yamlName ( structField ) if ! fpm . fieldPresent ( fieldName ) { continue } switch srcVal . Kind ( ) { case reflect . Map : if isZero ( srcVal ) { dst . Field ( i ) . Set ( reflect . MakeMap ( srcVal . Type ( ) ) ) continue } dstMap := dst . Field ( i ) if dstMap . IsNil ( ) { dstMap . Set ( reflect . MakeMap ( srcVal . Type ( ) ) ) } for _ , srcKey := range srcVal . MapKeys ( ) { srcMapVal := srcVal . MapIndex ( srcKey ) if isZero ( srcMapVal ) && isNonScalar ( srcMapVal ) { dstMap . SetMapIndex ( srcKey , reflect . Value { } ) continue } dstMap . SetMapIndex ( srcKey , srcMapVal ) } case reflect . Slice : dst . Field ( i ) . Set ( srcVal ) default : dst . Field ( i ) . Set ( srcVal ) } } }
mergeStructs iterates the fields of srcStruct and merges them into the
equivalent fields of dstStruct using the following rules.
mergeStructs iterates the fields of srcStruct and merges them into the equivalent fields of dstStruct using the following rules .
// Dereference pointers // Sanity check // Skip non-exportable fields // If a nil/empty map is provided then clear the destination map. // If the key points to an empty non-scalar value delete it from the dst map // Setting an empty value effectively deletes the key from the map