mirror of https://github.com/docker/cli.git
Fix error with merge composefile with networks…
… and other cases too. Updating mergo fixes the bugs (but introduced a slight behaviour change that had to be fixed too) Signed-off-by: Vincent Demeester <vincent@sbr.pm>
This commit is contained in:
parent
17aaad6f06
commit
0122730faf
|
@ -61,7 +61,7 @@ func mergeServices(base, override []types.ServiceConfig) ([]types.ServiceConfig,
|
||||||
}
|
}
|
||||||
for name, overrideService := range overrideServices {
|
for name, overrideService := range overrideServices {
|
||||||
if baseService, ok := baseServices[name]; ok {
|
if baseService, ok := baseServices[name]; ok {
|
||||||
if err := mergo.Merge(&baseService, &overrideService, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
|
if err := mergo.Merge(&baseService, &overrideService, mergo.WithAppendSlice, mergo.WithOverride, mergo.WithTransformers(specials)); err != nil {
|
||||||
return base, errors.Wrapf(err, "cannot merge service %s", name)
|
return base, errors.Wrapf(err, "cannot merge service %s", name)
|
||||||
}
|
}
|
||||||
baseServices[name] = baseService
|
baseServices[name] = baseService
|
||||||
|
@ -213,21 +213,21 @@ func mapByName(services []types.ServiceConfig) map[string]types.ServiceConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
|
func mergeVolumes(base, override map[string]types.VolumeConfig) (map[string]types.VolumeConfig, error) {
|
||||||
err := mergo.Map(&base, &override)
|
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||||
return base, err
|
return base, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
|
func mergeNetworks(base, override map[string]types.NetworkConfig) (map[string]types.NetworkConfig, error) {
|
||||||
err := mergo.Map(&base, &override)
|
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||||
return base, err
|
return base, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
|
func mergeSecrets(base, override map[string]types.SecretConfig) (map[string]types.SecretConfig, error) {
|
||||||
err := mergo.Map(&base, &override)
|
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||||
return base, err
|
return base, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
|
func mergeConfigs(base, override map[string]types.ConfigObjConfig) (map[string]types.ConfigObjConfig, error) {
|
||||||
err := mergo.Map(&base, &override)
|
err := mergo.Map(&base, &override, mergo.WithOverride)
|
||||||
return base, err
|
return base, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -711,7 +711,7 @@ func TestLoadMultipleUlimits(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadMultipleNetworks(t *testing.T) {
|
func TestLoadMultipleServiceNetworks(t *testing.T) {
|
||||||
networkCases := []struct {
|
networkCases := []struct {
|
||||||
name string
|
name string
|
||||||
networkBase map[string]interface{}
|
networkBase map[string]interface{}
|
||||||
|
@ -943,3 +943,74 @@ func TestLoadMultipleConfigs(t *testing.T) {
|
||||||
Configs: map[string]types.ConfigObjConfig{},
|
Configs: map[string]types.ConfigObjConfig{},
|
||||||
}, config)
|
}, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Issue#972
|
||||||
|
func TestLoadMultipleNetworks(t *testing.T) {
|
||||||
|
base := map[string]interface{}{
|
||||||
|
"version": "3.4",
|
||||||
|
"services": map[string]interface{}{
|
||||||
|
"foo": map[string]interface{}{
|
||||||
|
"image": "baz",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"volumes": map[string]interface{}{},
|
||||||
|
"networks": map[string]interface{}{
|
||||||
|
"hostnet": map[string]interface{}{
|
||||||
|
"driver": "overlay",
|
||||||
|
"ipam": map[string]interface{}{
|
||||||
|
"driver": "default",
|
||||||
|
"config": []interface{}{
|
||||||
|
map[string]interface{}{
|
||||||
|
"subnet": "10.0.0.0/20",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"secrets": map[string]interface{}{},
|
||||||
|
"configs": map[string]interface{}{},
|
||||||
|
}
|
||||||
|
override := map[string]interface{}{
|
||||||
|
"version": "3.4",
|
||||||
|
"services": map[string]interface{}{},
|
||||||
|
"volumes": map[string]interface{}{},
|
||||||
|
"networks": map[string]interface{}{
|
||||||
|
"hostnet": map[string]interface{}{
|
||||||
|
"external": map[string]interface{}{
|
||||||
|
"name": "host",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"secrets": map[string]interface{}{},
|
||||||
|
"configs": map[string]interface{}{},
|
||||||
|
}
|
||||||
|
configDetails := types.ConfigDetails{
|
||||||
|
ConfigFiles: []types.ConfigFile{
|
||||||
|
{Filename: "base.yml", Config: base},
|
||||||
|
{Filename: "override.yml", Config: override},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
config, err := Load(configDetails)
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, &types.Config{
|
||||||
|
Filename: "base.yml",
|
||||||
|
Version: "3.4",
|
||||||
|
Services: []types.ServiceConfig{
|
||||||
|
{
|
||||||
|
Name: "foo",
|
||||||
|
Image: "baz",
|
||||||
|
Environment: types.MappingWithEquals{},
|
||||||
|
}},
|
||||||
|
Networks: map[string]types.NetworkConfig{
|
||||||
|
"hostnet": {
|
||||||
|
Name: "host",
|
||||||
|
External: types.External{
|
||||||
|
External: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Volumes: map[string]types.VolumeConfig{},
|
||||||
|
Secrets: map[string]types.SecretConfig{},
|
||||||
|
Configs: map[string]types.ConfigObjConfig{},
|
||||||
|
}, config)
|
||||||
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ github.com/go-openapi/swag 1d0bd113de87027671077d3c71eb3ac5d7dbba72
|
||||||
github.com/gregjones/httpcache c1f8028e62adb3d518b823a2f8e6a95c38bdd3aa
|
github.com/gregjones/httpcache c1f8028e62adb3d518b823a2f8e6a95c38bdd3aa
|
||||||
github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b
|
github.com/grpc-ecosystem/grpc-gateway 1a03ca3bad1e1ebadaedd3abb76bc58d4ac8143b
|
||||||
github.com/howeyc/gopass 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
|
github.com/howeyc/gopass 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d
|
||||||
github.com/imdario/mergo ea74e0177b4df59af68c076af5008b427d00d40f
|
github.com/imdario/mergo 9d5f1277e9a8ed20c3684bda8fde67c05628518c # v0.3.4
|
||||||
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
|
||||||
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
|
github.com/juju/ratelimit 5b9ff866471762aa2ab2dced63c9fb6f53921342
|
||||||
github.com/json-iterator/go 6240e1e7983a85228f7fd9c3e1b6932d46ec58e2
|
github.com/json-iterator/go 6240e1e7983a85228f7fd9c3e1b6932d46ec58e2
|
||||||
|
|
|
@ -8,10 +8,11 @@ Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the
|
||||||
|
|
||||||
It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild).
|
It is ready for production use. [It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc](https://github.com/imdario/mergo#mergo-in-the-wild).
|
||||||
|
|
||||||
[![Build Status][1]][2]
|
|
||||||
[![GoDoc][3]][4]
|
[![GoDoc][3]][4]
|
||||||
[![GoCard][5]][6]
|
[![GoCard][5]][6]
|
||||||
|
[![Build Status][1]][2]
|
||||||
[![Coverage Status][7]][8]
|
[![Coverage Status][7]][8]
|
||||||
|
[![Sourcegraph][9]][10]
|
||||||
|
|
||||||
[1]: https://travis-ci.org/imdario/mergo.png
|
[1]: https://travis-ci.org/imdario/mergo.png
|
||||||
[2]: https://travis-ci.org/imdario/mergo
|
[2]: https://travis-ci.org/imdario/mergo
|
||||||
|
@ -21,18 +22,22 @@ It is ready for production use. [It is used in several projects by Docker, Googl
|
||||||
[6]: https://goreportcard.com/report/github.com/imdario/mergo
|
[6]: https://goreportcard.com/report/github.com/imdario/mergo
|
||||||
[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master
|
[7]: https://coveralls.io/repos/github/imdario/mergo/badge.svg?branch=master
|
||||||
[8]: https://coveralls.io/github/imdario/mergo?branch=master
|
[8]: https://coveralls.io/github/imdario/mergo?branch=master
|
||||||
|
[9]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg
|
||||||
|
[10]: https://sourcegraph.com/github.com/imdario/mergo?badge
|
||||||
|
|
||||||
### Latest release
|
### Latest release
|
||||||
|
|
||||||
[Release 0.3.2](https://github.com/imdario/mergo/releases/tag/0.3.2) is an important release because it changes `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code.
|
[Release v0.3.4](https://github.com/imdario/mergo/releases/tag/v0.3.4).
|
||||||
|
|
||||||
### Important note
|
### Important note
|
||||||
|
|
||||||
|
Please keep in mind that in [0.3.2](//github.com/imdario/mergo/releases/tag/0.3.2) Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). An optional/variadic argument has been added, so it won't break existing code.
|
||||||
|
|
||||||
If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0).
|
If you were using Mergo **before** April 6th 2015, please check your project works as intended after updating your local copy with ```go get -u github.com/imdario/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause (I hope it won't!) in existing projects after the change (release 0.2.0).
|
||||||
|
|
||||||
### Donations
|
### Donations
|
||||||
|
|
||||||
If Mergo is useful to you, consider buying me a coffe, a beer or making a monthly donation so I can keep building great free software. :heart_eyes:
|
If Mergo is useful to you, consider buying me a coffee, a beer or making a monthly donation so I can keep building great free software. :heart_eyes:
|
||||||
|
|
||||||
<a href='https://ko-fi.com/B0B58839' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
|
<a href='https://ko-fi.com/B0B58839' target='_blank'><img height='36' style='border:0px;height:36px;' src='https://az743702.vo.msecnd.net/cdn/kofi1.png?v=0' border='0' alt='Buy Me a Coffee at ko-fi.com' /></a>
|
||||||
[![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo)
|
[![Beerpay](https://beerpay.io/imdario/mergo/badge.svg)](https://beerpay.io/imdario/mergo)
|
||||||
|
@ -93,7 +98,7 @@ If Mergo is useful to you, consider buying me a coffe, a beer or making a monthl
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
|
You can only merge same-type structs with exported fields initialized as zero value of their type and same-types maps. Mergo won't merge unexported (private) fields but will do recursively any exported one. It won't merge empty structs value as [they are not considered zero values](https://golang.org/ref/spec#The_zero_value) either. Also maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if err := mergo.Merge(&dst, src); err != nil {
|
if err := mergo.Merge(&dst, src); err != nil {
|
||||||
|
@ -104,7 +109,7 @@ if err := mergo.Merge(&dst, src); err != nil {
|
||||||
Also, you can merge overwriting values using the transformer `WithOverride`.
|
Also, you can merge overwriting values using the transformer `WithOverride`.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
if err := mergo.Merge(&dst, src, WithOverride); err != nil {
|
if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -164,6 +169,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/imdario/mergo"
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -195,7 +201,7 @@ type Snapshot struct {
|
||||||
func main() {
|
func main() {
|
||||||
src := Snapshot{time.Now()}
|
src := Snapshot{time.Now()}
|
||||||
dest := Snapshot{}
|
dest := Snapshot{}
|
||||||
mergo.Merge(&dest, src, WithTransformers(timeTransfomer{}))
|
mergo.Merge(&dest, src, mergo.WithTransformers(timeTransfomer{}))
|
||||||
fmt.Println(dest)
|
fmt.Println(dest)
|
||||||
// Will print
|
// Will print
|
||||||
// { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
|
// { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
|
||||||
|
|
|
@ -31,8 +31,8 @@ func isExported(field reflect.StructField) bool {
|
||||||
// Traverses recursively both values, assigning src's fields values to dst.
|
// Traverses recursively both values, assigning src's fields values to dst.
|
||||||
// The map argument tracks comparisons that have already been seen, which allows
|
// The map argument tracks comparisons that have already been seen, which allows
|
||||||
// short circuiting on recursive types.
|
// short circuiting on recursive types.
|
||||||
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *config) (err error) {
|
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
|
||||||
overwrite := config.overwrite
|
overwrite := config.Overwrite
|
||||||
if dst.CanAddr() {
|
if dst.CanAddr() {
|
||||||
addr := dst.UnsafeAddr()
|
addr := dst.UnsafeAddr()
|
||||||
h := 17 * addr
|
h := 17 * addr
|
||||||
|
@ -128,23 +128,23 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, conf
|
||||||
// doesn't apply if dst is a map.
|
// doesn't apply if dst is a map.
|
||||||
// This is separated method from Merge because it is cleaner and it keeps sane
|
// This is separated method from Merge because it is cleaner and it keeps sane
|
||||||
// semantics: merging equal types, mapping different (restricted) types.
|
// semantics: merging equal types, mapping different (restricted) types.
|
||||||
func Map(dst, src interface{}, opts ...func(*config)) error {
|
func Map(dst, src interface{}, opts ...func(*Config)) error {
|
||||||
return _map(dst, src, opts...)
|
return _map(dst, src, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overriden by
|
// MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
|
||||||
// non-empty src attribute values.
|
// non-empty src attribute values.
|
||||||
// Deprecated: Use Map(…) with WithOverride
|
// Deprecated: Use Map(…) with WithOverride
|
||||||
func MapWithOverwrite(dst, src interface{}, opts ...func(*config)) error {
|
func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
|
||||||
return _map(dst, src, append(opts, WithOverride)...)
|
return _map(dst, src, append(opts, WithOverride)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func _map(dst, src interface{}, opts ...func(*config)) error {
|
func _map(dst, src interface{}, opts ...func(*Config)) error {
|
||||||
var (
|
var (
|
||||||
vDst, vSrc reflect.Value
|
vDst, vSrc reflect.Value
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
config := &config{}
|
config := &Config{}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(config)
|
opt(config)
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
package mergo
|
package mergo
|
||||||
|
|
||||||
import "reflect"
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
func hasExportedField(dst reflect.Value) (exported bool) {
|
func hasExportedField(dst reflect.Value) (exported bool) {
|
||||||
for i, n := 0, dst.NumField(); i < n; i++ {
|
for i, n := 0, dst.NumField(); i < n; i++ {
|
||||||
|
@ -22,20 +24,21 @@ func hasExportedField(dst reflect.Value) (exported bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
type config struct {
|
type Config struct {
|
||||||
overwrite bool
|
Overwrite bool
|
||||||
transformers transformers
|
AppendSlice bool
|
||||||
|
Transformers Transformers
|
||||||
}
|
}
|
||||||
|
|
||||||
type transformers interface {
|
type Transformers interface {
|
||||||
Transformer(reflect.Type) func(dst, src reflect.Value) error
|
Transformer(reflect.Type) func(dst, src reflect.Value) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// Traverses recursively both values, assigning src's fields values to dst.
|
// Traverses recursively both values, assigning src's fields values to dst.
|
||||||
// The map argument tracks comparisons that have already been seen, which allows
|
// The map argument tracks comparisons that have already been seen, which allows
|
||||||
// short circuiting on recursive types.
|
// short circuiting on recursive types.
|
||||||
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *config) (err error) {
|
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
|
||||||
overwrite := config.overwrite
|
overwrite := config.Overwrite
|
||||||
|
|
||||||
if !src.IsValid() {
|
if !src.IsValid() {
|
||||||
return
|
return
|
||||||
|
@ -54,8 +57,8 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||||
visited[h] = &visit{addr, typ, seen}
|
visited[h] = &visit{addr, typ, seen}
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.transformers != nil && !isEmptyValue(dst) {
|
if config.Transformers != nil && !isEmptyValue(dst) {
|
||||||
if fn := config.transformers.Transformer(dst.Type()); fn != nil {
|
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
|
||||||
err = fn(dst, src)
|
err = fn(dst, src)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -75,9 +78,8 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
if len(src.MapKeys()) == 0 && !src.IsNil() && len(dst.MapKeys()) == 0 {
|
if dst.IsNil() && !src.IsNil() {
|
||||||
dst.Set(reflect.MakeMap(dst.Type()))
|
dst.Set(reflect.MakeMap(dst.Type()))
|
||||||
return
|
|
||||||
}
|
}
|
||||||
for _, key := range src.MapKeys() {
|
for _, key := range src.MapKeys() {
|
||||||
srcElement := src.MapIndex(key)
|
srcElement := src.MapIndex(key)
|
||||||
|
@ -130,7 +132,14 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
dst.Set(reflect.AppendSlice(dst, src))
|
if !dst.CanSet() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !isEmptyValue(src) && (overwrite || isEmptyValue(dst)) && !config.AppendSlice {
|
||||||
|
dst.Set(src)
|
||||||
|
} else {
|
||||||
|
dst.Set(reflect.AppendSlice(dst, src))
|
||||||
|
}
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
fallthrough
|
fallthrough
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
|
@ -174,36 +183,41 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, co
|
||||||
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
|
// src attributes if they themselves are not empty. dst and src must be valid same-type structs
|
||||||
// and dst must be a pointer to struct.
|
// and dst must be a pointer to struct.
|
||||||
// It won't merge unexported (private) fields and will do recursively any exported field.
|
// It won't merge unexported (private) fields and will do recursively any exported field.
|
||||||
func Merge(dst, src interface{}, opts ...func(*config)) error {
|
func Merge(dst, src interface{}, opts ...func(*Config)) error {
|
||||||
return merge(dst, src, opts...)
|
return merge(dst, src, opts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
|
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
|
||||||
// non-empty src attribute values.
|
// non-empty src attribute values.
|
||||||
// Deprecated: use Merge(…) with WithOverride
|
// Deprecated: use Merge(…) with WithOverride
|
||||||
func MergeWithOverwrite(dst, src interface{}, opts ...func(*config)) error {
|
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
|
||||||
return merge(dst, src, append(opts, WithOverride)...)
|
return merge(dst, src, append(opts, WithOverride)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
|
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
|
||||||
func WithTransformers(transformers transformers) func(*config) {
|
func WithTransformers(transformers Transformers) func(*Config) {
|
||||||
return func(config *config) {
|
return func(config *Config) {
|
||||||
config.transformers = transformers
|
config.Transformers = transformers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
|
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
|
||||||
func WithOverride(config *config) {
|
func WithOverride(config *Config) {
|
||||||
config.overwrite = true
|
config.Overwrite = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func merge(dst, src interface{}, opts ...func(*config)) error {
|
// WithAppendSlice will make merge append slices instead of overwriting it
|
||||||
|
func WithAppendSlice(config *Config) {
|
||||||
|
config.AppendSlice = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func merge(dst, src interface{}, opts ...func(*Config)) error {
|
||||||
var (
|
var (
|
||||||
vDst, vSrc reflect.Value
|
vDst, vSrc reflect.Value
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
config := &config{}
|
config := &Config{}
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
opt(config)
|
opt(config)
|
||||||
|
|
Loading…
Reference in New Issue