Fix the dependency issue (#231)

This commit is contained in:
Robbie Zhang
2018-06-21 12:09:42 -07:00
committed by GitHub
parent 027b76651d
commit 6ec1098bb8
16629 changed files with 74837 additions and 4975021 deletions

View File

@@ -1,2 +0,0 @@
language: go
install: go get -t

View File

@@ -1,46 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at i@dario.im. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

View File

@@ -1,141 +0,0 @@
# Mergo
A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region Marche.
![Mergo dall'alto](http://www.comune.mergo.an.it/Siti/Mergo/Immagini/Foto/mergo_dall_alto.jpg)
## Status
It is ready for production use. It works fine after extensive use in the wild.
[![Build Status][1]][2]
[![GoDoc][3]][4]
[![GoCard][5]][6]
[1]: https://travis-ci.org/imdario/mergo.png
[2]: https://travis-ci.org/imdario/mergo
[3]: https://godoc.org/github.com/imdario/mergo?status.svg
[4]: https://godoc.org/github.com/imdario/mergo
[5]: https://goreportcard.com/badge/imdario/mergo
[6]: https://goreportcard.com/report/github.com/imdario/mergo
### Important note
Mergo is intended to assign **only** zero value fields on destination with source value. Since April 6th it works like this. Before it didn't work properly, causing some random overwrites. After some issues and PRs I found it didn't merge as I designed it. Thanks to [imdario/mergo#8](https://github.com/imdario/mergo/pull/8) overwriting functions were added and the wrong behavior was clearly detected.
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).
### Mergo in the wild
- [docker/docker](https://github.com/docker/docker/)
- [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
- [imdario/zas](https://github.com/imdario/zas)
- [soniah/dnsmadeeasy](https://github.com/soniah/dnsmadeeasy)
- [EagerIO/Stout](https://github.com/EagerIO/Stout)
- [lynndylanhurley/defsynth-api](https://github.com/lynndylanhurley/defsynth-api)
- [russross/canvasassignments](https://github.com/russross/canvasassignments)
- [rdegges/cryptly-api](https://github.com/rdegges/cryptly-api)
- [casualjim/exeggutor](https://github.com/casualjim/exeggutor)
- [divshot/gitling](https://github.com/divshot/gitling)
- [RWJMurphy/gorl](https://github.com/RWJMurphy/gorl)
- [andrerocker/deploy42](https://github.com/andrerocker/deploy42)
- [elwinar/rambler](https://github.com/elwinar/rambler)
- [tmaiaroto/gopartman](https://github.com/tmaiaroto/gopartman)
- [jfbus/impressionist](https://github.com/jfbus/impressionist)
- [Jmeyering/zealot](https://github.com/Jmeyering/zealot)
- [godep-migrator/rigger-host](https://github.com/godep-migrator/rigger-host)
- [Dronevery/MultiwaySwitch-Go](https://github.com/Dronevery/MultiwaySwitch-Go)
- [thoas/picfit](https://github.com/thoas/picfit)
- [mantasmatelis/whooplist-server](https://github.com/mantasmatelis/whooplist-server)
- [jnuthong/item_search](https://github.com/jnuthong/item_search)
- [Iris Web Framework](https://github.com/kataras/iris)
## Installation
go get github.com/imdario/mergo
// use in your .go code
import (
"github.com/imdario/mergo"
)
## 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).
```go
if err := mergo.Merge(&dst, src); err != nil {
// ...
}
```
Also, you can merge overwriting values using MergeWithOverwrite.
```go
if err := mergo.MergeWithOverwrite(&dst, src); err != nil {
// ...
}
```
Additionally, you can map a map[string]interface{} to a struct (and otherwise, from struct to map), following the same restrictions as in Merge(). Keys are capitalized to find each corresponding exported field.
```go
if err := mergo.Map(&dst, srcMap); err != nil {
// ...
}
```
Warning: if you map a struct to map, it won't do it recursively. Don't expect Mergo to map struct members of your struct as map[string]interface{}. They will be just assigned as values.
More information and examples in [godoc documentation](http://godoc.org/github.com/imdario/mergo).
### Nice example
```go
package main
import (
"fmt"
"github.com/imdario/mergo"
)
type Foo struct {
A string
B int64
}
func main() {
src := Foo{
A: "one",
B: 2,
}
dest := Foo{
A: "two",
}
mergo.Merge(&dest, src)
fmt.Println(dest)
// Will print
// {two 2}
}
```
Note: if test are failing due missing package, please execute:
go get gopkg.in/yaml.v1
## Contact me
If I can help you, you have an idea or you are using Mergo in your projects, don't hesitate to drop me a line (or a pull request): [@im_dario](https://twitter.com/im_dario)
## About
Written by [Dario Castañé](http://dario.im).
## License
[BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).

View File

@@ -1,25 +0,0 @@
package mergo
import (
"encoding/json"
"testing"
)
var (
request = `{"timestamp":null, "name": "foo"}`
maprequest = map[string]interface{}{
"timestamp": nil,
"name": "foo",
"newStuff": "foo",
}
)
func TestIssue17MergeWithOverwrite(t *testing.T) {
var something map[string]interface{}
if err := json.Unmarshal([]byte(request), &something); err != nil {
t.Errorf("Error while Unmarshalling maprequest %s", err)
}
if err := MergeWithOverwrite(&something, maprequest); err != nil {
t.Errorf("Error while merging %s", err)
}
}

View File

@@ -1,27 +0,0 @@
package mergo
import (
"testing"
"time"
)
type document struct {
Created *time.Time
}
func TestIssue23MergeWithOverwrite(t *testing.T) {
now := time.Now()
dst := document{
&now,
}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := document{
&expected,
}
if err := MergeWithOverwrite(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created != src.Created {
t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
}
}

View File

@@ -1,59 +0,0 @@
package mergo
import (
"testing"
"time"
)
type structWithoutTimePointer struct {
Created time.Time
}
func TestIssue38Merge(t *testing.T) {
dst := structWithoutTimePointer{
time.Now(),
}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := structWithoutTimePointer{
expected,
}
if err := Merge(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created == src.Created {
t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
}
}
func TestIssue38MergeEmptyStruct(t *testing.T) {
dst := structWithoutTimePointer{}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := structWithoutTimePointer{
expected,
}
if err := Merge(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created == src.Created {
t.Fatalf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
}
}
func TestIssue38MergeWithOverwrite(t *testing.T) {
dst := structWithoutTimePointer{
time.Now(),
}
expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
src := structWithoutTimePointer{
expected,
}
if err := MergeWithOverwrite(&dst, src); err != nil {
t.Errorf("Error while merging %s", err)
}
if dst.Created != src.Created {
t.Fatalf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
}
}

View File

@@ -31,7 +31,8 @@ func isExported(field reflect.StructField) bool {
// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
overwrite := config.Overwrite
if dst.CanAddr() {
addr := dst.UnsafeAddr()
h := 17 * addr
@@ -97,15 +98,15 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
continue
}
if srcKind == dstKind {
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else if srcKind == reflect.Map {
if err = deepMap(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
} else {
@@ -127,28 +128,35 @@ func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, over
// doesn't apply if dst is a map.
// This is separated method from Merge because it is cleaner and it keeps sane
// semantics: merging equal types, mapping different (restricted) types.
func Map(dst, src interface{}) error {
return _map(dst, src, false)
func Map(dst, src interface{}, opts ...func(*Config)) error {
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.
func MapWithOverwrite(dst, src interface{}) error {
return _map(dst, src, true)
// Deprecated: Use Map(…) with WithOverride
func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
return _map(dst, src, append(opts, WithOverride)...)
}
func _map(dst, src interface{}, overwrite bool) error {
func _map(dst, src interface{}, opts ...func(*Config)) error {
var (
vDst, vSrc reflect.Value
err error
)
config := &Config{}
for _, opt := range opts {
opt(config)
}
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
return err
}
// To be friction-less, we redirect equal-type arguments
// to deepMerge. Only because arguments can be anything.
if vSrc.Kind() == vDst.Kind() {
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}
switch vSrc.Kind() {
case reflect.Struct:
@@ -162,5 +170,5 @@ func _map(dst, src interface{}, overwrite bool) error {
default:
return ErrNotSupported
}
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

View File

@@ -15,7 +15,7 @@ import (
func hasExportedField(dst reflect.Value) (exported bool) {
for i, n := 0, dst.NumField(); i < n; i++ {
field := dst.Type().Field(i)
if field.Anonymous {
if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
exported = exported || hasExportedField(dst.Field(i))
} else {
exported = exported || len(field.PkgPath) == 0
@@ -24,10 +24,22 @@ func hasExportedField(dst reflect.Value) (exported bool) {
return
}
type Config struct {
Overwrite bool
AppendSlice bool
Transformers Transformers
}
type Transformers interface {
Transformer(reflect.Type) func(dst, src reflect.Value) error
}
// Traverses recursively both values, assigning src's fields values to dst.
// The map argument tracks comparisons that have already been seen, which allows
// short circuiting on recursive types.
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, overwrite bool) (err error) {
func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
overwrite := config.Overwrite
if !src.IsValid() {
return
}
@@ -44,11 +56,19 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
// Remember, remember...
visited[h] = &visit{addr, typ, seen}
}
if config.Transformers != nil && !isEmptyValue(dst) {
if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
err = fn(dst, src)
return
}
}
switch dst.Kind() {
case reflect.Struct:
if hasExportedField(dst) {
for i, n := 0, dst.NumField(); i < n; i++ {
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, overwrite); err != nil {
if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
return
}
}
@@ -58,9 +78,8 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
}
}
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()))
return
}
for _, key := range src.MapKeys() {
srcElement := src.MapIndex(key)
@@ -69,7 +88,7 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
}
dstElement := dst.MapIndex(key)
switch srcElement.Kind() {
case reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Slice:
case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
if srcElement.IsNil() {
continue
}
@@ -84,36 +103,60 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
case reflect.Ptr:
fallthrough
case reflect.Map:
if err = deepMerge(dstElement, srcElement, visited, depth+1, overwrite); err != nil {
if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
return
}
case reflect.Slice:
srcSlice := reflect.ValueOf(srcElement.Interface())
var dstSlice reflect.Value
if !dstElement.IsValid() || dstElement.IsNil() {
dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
} else {
dstSlice = reflect.ValueOf(dstElement.Interface())
}
dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
dst.SetMapIndex(key, dstSlice)
}
}
if dstElement.IsValid() && reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map {
continue
}
if !isEmptyValue(srcElement) && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
if srcElement.IsValid() && (overwrite || (!dstElement.IsValid() || isEmptyValue(dst))) {
if dst.IsNil() {
dst.Set(reflect.MakeMap(dst.Type()))
}
dst.SetMapIndex(key, srcElement)
}
}
case reflect.Slice:
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:
fallthrough
case reflect.Interface:
if src.IsNil() {
break
}
if src.Kind() != reflect.Interface {
if dst.IsNil() || overwrite {
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
dst.Set(src)
}
} else if src.Kind() == reflect.Ptr {
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
} else if dst.Elem().Type() == src.Type() {
if err = deepMerge(dst.Elem(), src, visited, depth+1, overwrite); err != nil {
if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
return
}
} else {
@@ -121,13 +164,11 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
}
break
}
if src.IsNil() {
break
} else if dst.IsNil() || overwrite {
if dst.IsNil() || overwrite {
if dst.CanSet() && (overwrite || isEmptyValue(dst)) {
dst.Set(src)
}
} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, overwrite); err != nil {
} else if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
return
}
default:
@@ -142,26 +183,51 @@ func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, ov
// 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.
// It won't merge unexported (private) fields and will do recursively any exported field.
func Merge(dst, src interface{}) error {
return merge(dst, src, false)
func Merge(dst, src interface{}, opts ...func(*Config)) error {
return merge(dst, src, opts...)
}
// MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overriden by
// non-empty src attribute values.
func MergeWithOverwrite(dst, src interface{}) error {
return merge(dst, src, true)
// Deprecated: use Merge(…) with WithOverride
func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
return merge(dst, src, append(opts, WithOverride)...)
}
func merge(dst, src interface{}, overwrite bool) error {
// WithTransformers adds transformers to merge, allowing to customize the merging of some types.
func WithTransformers(transformers Transformers) func(*Config) {
return func(config *Config) {
config.Transformers = transformers
}
}
// WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
func WithOverride(config *Config) {
config.Overwrite = true
}
// 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 (
vDst, vSrc reflect.Value
err error
)
config := &Config{}
for _, opt := range opts {
opt(config)
}
if vDst, vSrc, err = resolveValues(dst, src); err != nil {
return err
}
if vDst.Type() != vSrc.Type() {
return ErrDifferentArgumentsTypes
}
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, overwrite)
return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
}

View File

@@ -32,7 +32,7 @@ type visit struct {
next *visit
}
// From src/pkg/encoding/json.
// From src/pkg/encoding/json/encode.go.
func isEmptyValue(v reflect.Value) bool {
switch v.Kind() {
case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
@@ -47,6 +47,8 @@ func isEmptyValue(v reflect.Value) bool {
return v.Float() == 0
case reflect.Interface, reflect.Ptr, reflect.Func:
return v.IsNil()
case reflect.Invalid:
return true
}
return false
}

View File

@@ -1,662 +0,0 @@
// Copyright 2013 Dario Castañé. All rights reserved.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package mergo
import (
"gopkg.in/yaml.v2"
"io/ioutil"
"reflect"
"testing"
"time"
)
type simpleTest struct {
Value int
}
type complexTest struct {
St simpleTest
sz int
ID string
}
type mapTest struct {
M map[int]int
}
type ifcTest struct {
I interface{}
}
type moreComplextText struct {
Ct complexTest
St simpleTest
Nt simpleTest
}
type pointerTest struct {
C *simpleTest
}
type sliceTest struct {
S []int
}
func TestKb(t *testing.T) {
type testStruct struct {
Name string
KeyValue map[string]interface{}
}
akv := make(map[string]interface{})
akv["Key1"] = "not value 1"
akv["Key2"] = "value2"
a := testStruct{}
a.Name = "A"
a.KeyValue = akv
bkv := make(map[string]interface{})
bkv["Key1"] = "value1"
bkv["Key3"] = "value3"
b := testStruct{}
b.Name = "B"
b.KeyValue = bkv
ekv := make(map[string]interface{})
ekv["Key1"] = "value1"
ekv["Key2"] = "value2"
ekv["Key3"] = "value3"
expected := testStruct{}
expected.Name = "B"
expected.KeyValue = ekv
Merge(&b, a)
if !reflect.DeepEqual(b, expected) {
t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected)
}
}
func TestNil(t *testing.T) {
if err := Merge(nil, nil); err != ErrNilArguments {
t.Fail()
}
}
func TestDifferentTypes(t *testing.T) {
a := simpleTest{42}
b := 42
if err := Merge(&a, b); err != ErrDifferentArgumentsTypes {
t.Fail()
}
}
func TestSimpleStruct(t *testing.T) {
a := simpleTest{}
b := simpleTest{42}
if err := Merge(&a, b); err != nil {
t.FailNow()
}
if a.Value != 42 {
t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value)
}
if !reflect.DeepEqual(a, b) {
t.FailNow()
}
}
func TestComplexStruct(t *testing.T) {
a := complexTest{}
a.ID = "athing"
b := complexTest{simpleTest{42}, 1, "bthing"}
if err := Merge(&a, b); err != nil {
t.FailNow()
}
if a.St.Value != 42 {
t.Fatalf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value)
}
if a.sz == 1 {
t.Fatalf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz)
}
if a.ID == b.ID {
t.Fatalf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID)
}
}
func TestComplexStructWithOverwrite(t *testing.T) {
a := complexTest{simpleTest{1}, 1, "do-not-overwrite-with-empty-value"}
b := complexTest{simpleTest{42}, 2, ""}
expect := complexTest{simpleTest{42}, 1, "do-not-overwrite-with-empty-value"}
if err := MergeWithOverwrite(&a, b); err != nil {
t.FailNow()
}
if !reflect.DeepEqual(a, expect) {
t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", a, expect)
}
}
func TestPointerStruct(t *testing.T) {
s1 := simpleTest{}
s2 := simpleTest{19}
a := pointerTest{&s1}
b := pointerTest{&s2}
if err := Merge(&a, b); err != nil {
t.FailNow()
}
if a.C.Value != b.C.Value {
t.Fatalf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
}
}
type embeddingStruct struct {
embeddedStruct
}
type embeddedStruct struct {
A string
}
func TestEmbeddedStruct(t *testing.T) {
tests := []struct {
src embeddingStruct
dst embeddingStruct
expected embeddingStruct
}{
{
src: embeddingStruct{
embeddedStruct{"foo"},
},
dst: embeddingStruct{
embeddedStruct{""},
},
expected: embeddingStruct{
embeddedStruct{"foo"},
},
},
{
src: embeddingStruct{
embeddedStruct{""},
},
dst: embeddingStruct{
embeddedStruct{"bar"},
},
expected: embeddingStruct{
embeddedStruct{"bar"},
},
},
{
src: embeddingStruct{
embeddedStruct{"foo"},
},
dst: embeddingStruct{
embeddedStruct{"bar"},
},
expected: embeddingStruct{
embeddedStruct{"bar"},
},
},
}
for _, test := range tests {
err := Merge(&test.dst, test.src)
if err != nil {
t.Errorf("unexpected error: %v", err)
continue
}
if !reflect.DeepEqual(test.dst, test.expected) {
t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst)
}
}
}
func TestPointerStructNil(t *testing.T) {
a := pointerTest{nil}
b := pointerTest{&simpleTest{19}}
if err := Merge(&a, b); err != nil {
t.FailNow()
}
if a.C.Value != b.C.Value {
t.Fatalf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
}
}
func TestSliceStruct(t *testing.T) {
a := sliceTest{}
b := sliceTest{[]int{1, 2, 3}}
if err := Merge(&a, b); err != nil {
t.FailNow()
}
if len(b.S) != 3 {
t.FailNow()
}
if len(a.S) != len(b.S) {
t.Fatalf("b not merged in a proper way %d != %d", len(a.S), len(b.S))
}
a = sliceTest{[]int{1}}
b = sliceTest{[]int{1, 2, 3}}
if err := Merge(&a, b); err != nil {
t.FailNow()
}
if len(a.S) != 1 {
t.FailNow()
}
if len(a.S) == len(b.S) {
t.Fatalf("b merged unexpectedly %d != %d", len(a.S), len(b.S))
}
}
func TestEmptyMaps(t *testing.T) {
a := mapTest{}
b := mapTest{
map[int]int{},
}
if err := Merge(&a, b); err != nil {
t.Fail()
}
if !reflect.DeepEqual(a, b) {
t.FailNow()
}
}
func TestEmptyToEmptyMaps(t *testing.T) {
a := mapTest{}
b := mapTest{}
if err := Merge(&a, b); err != nil {
t.Fail()
}
if !reflect.DeepEqual(a, b) {
t.FailNow()
}
}
func TestEmptyToNotEmptyMaps(t *testing.T) {
a := mapTest{map[int]int{
1: 2,
3: 4,
}}
aa := mapTest{map[int]int{
1: 2,
3: 4,
}}
b := mapTest{
map[int]int{},
}
if err := Merge(&a, b); err != nil {
t.Fail()
}
if !reflect.DeepEqual(a, aa) {
t.FailNow()
}
}
func TestMapsWithOverwrite(t *testing.T) {
m := map[string]simpleTest{
"a": {}, // overwritten by 16
"b": {42}, // not overwritten by empty value
"c": {13}, // overwritten by 12
"d": {61},
}
n := map[string]simpleTest{
"a": {16},
"b": {},
"c": {12},
"e": {14},
}
expect := map[string]simpleTest{
"a": {16},
"b": {},
"c": {12},
"d": {61},
"e": {14},
}
if err := MergeWithOverwrite(&m, n); err != nil {
t.Fatalf(err.Error())
}
if !reflect.DeepEqual(m, expect) {
t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
}
}
func TestMaps(t *testing.T) {
m := map[string]simpleTest{
"a": {},
"b": {42},
"c": {13},
"d": {61},
}
n := map[string]simpleTest{
"a": {16},
"b": {},
"c": {12},
"e": {14},
}
expect := map[string]simpleTest{
"a": {0},
"b": {42},
"c": {13},
"d": {61},
"e": {14},
}
if err := Merge(&m, n); err != nil {
t.Fatalf(err.Error())
}
if !reflect.DeepEqual(m, expect) {
t.Fatalf("Test failed:\ngot :\n%#v\n\nwant :\n%#v\n\n", m, expect)
}
if m["a"].Value != 0 {
t.Fatalf(`n merged in m because I solved non-addressable map values TODO: m["a"].Value(%d) != n["a"].Value(%d)`, m["a"].Value, n["a"].Value)
}
if m["b"].Value != 42 {
t.Fatalf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value)
}
if m["c"].Value != 13 {
t.Fatalf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value)
}
}
func TestYAMLMaps(t *testing.T) {
thing := loadYAML("testdata/thing.yml")
license := loadYAML("testdata/license.yml")
ft := thing["fields"].(map[interface{}]interface{})
fl := license["fields"].(map[interface{}]interface{})
// license has one extra field (site) and another already existing in thing (author) that Mergo won't override.
expectedLength := len(ft) + len(fl) - 1
if err := Merge(&license, thing); err != nil {
t.Fatal(err.Error())
}
currentLength := len(license["fields"].(map[interface{}]interface{}))
if currentLength != expectedLength {
t.Fatalf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength)
}
fields := license["fields"].(map[interface{}]interface{})
if _, ok := fields["id"]; !ok {
t.Fatalf(`thing not merged in license properly, license must have a new id field from thing`)
}
}
func TestTwoPointerValues(t *testing.T) {
a := &simpleTest{}
b := &simpleTest{42}
if err := Merge(a, b); err != nil {
t.Fatalf(`Boom. You crossed the streams: %s`, err)
}
}
func TestMap(t *testing.T) {
a := complexTest{}
a.ID = "athing"
c := moreComplextText{a, simpleTest{}, simpleTest{}}
b := map[string]interface{}{
"ct": map[string]interface{}{
"st": map[string]interface{}{
"value": 42,
},
"sz": 1,
"id": "bthing",
},
"st": &simpleTest{144}, // Mapping a reference
"zt": simpleTest{299}, // Mapping a missing field (zt doesn't exist)
"nt": simpleTest{3},
}
if err := Map(&c, b); err != nil {
t.FailNow()
}
m := b["ct"].(map[string]interface{})
n := m["st"].(map[string]interface{})
o := b["st"].(*simpleTest)
p := b["nt"].(simpleTest)
if c.Ct.St.Value != 42 {
t.Fatalf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"])
}
if c.St.Value != 144 {
t.Fatalf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value)
}
if c.Nt.Value != 3 {
t.Fatalf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value)
}
if c.Ct.sz == 1 {
t.Fatalf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"])
}
if c.Ct.ID == m["id"] {
t.Fatalf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"])
}
}
func TestSimpleMap(t *testing.T) {
a := simpleTest{}
b := map[string]interface{}{
"value": 42,
}
if err := Map(&a, b); err != nil {
t.FailNow()
}
if a.Value != 42 {
t.Fatalf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"])
}
}
func TestIfcMap(t *testing.T) {
a := ifcTest{}
b := ifcTest{42}
if err := Map(&a, b); err != nil {
t.FailNow()
}
if a.I != 42 {
t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
}
if !reflect.DeepEqual(a, b) {
t.FailNow()
}
}
func TestIfcMapNoOverwrite(t *testing.T) {
a := ifcTest{13}
b := ifcTest{42}
if err := Map(&a, b); err != nil {
t.FailNow()
}
if a.I != 13 {
t.Fatalf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I)
}
}
func TestIfcMapWithOverwrite(t *testing.T) {
a := ifcTest{13}
b := ifcTest{42}
if err := MapWithOverwrite(&a, b); err != nil {
t.FailNow()
}
if a.I != 42 {
t.Fatalf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
}
if !reflect.DeepEqual(a, b) {
t.FailNow()
}
}
type pointerMapTest struct {
A int
hidden int
B *simpleTest
}
func TestBackAndForth(t *testing.T) {
pt := pointerMapTest{42, 1, &simpleTest{66}}
m := make(map[string]interface{})
if err := Map(&m, pt); err != nil {
t.FailNow()
}
var (
v interface{}
ok bool
)
if v, ok = m["a"]; v.(int) != pt.A || !ok {
t.Fatalf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A)
}
if v, ok = m["b"]; !ok {
t.Fatalf("pt not merged in properly: B is missing in m")
}
var st *simpleTest
if st = v.(*simpleTest); st.Value != 66 {
t.Fatalf("something went wrong while mapping pt on m, B wasn't copied")
}
bpt := pointerMapTest{}
if err := Map(&bpt, m); err != nil {
t.Fatal(err)
}
if bpt.A != pt.A {
t.Fatalf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A)
}
if bpt.hidden == pt.hidden {
t.Fatalf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden)
}
if bpt.B.Value != pt.B.Value {
t.Fatalf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value)
}
}
func TestEmbeddedPointerUnpacking(t *testing.T) {
tests := []struct{ input pointerMapTest }{
{pointerMapTest{42, 1, nil}},
{pointerMapTest{42, 1, &simpleTest{66}}},
}
newValue := 77
m := map[string]interface{}{
"b": map[string]interface{}{
"value": newValue,
},
}
for _, test := range tests {
pt := test.input
if err := MapWithOverwrite(&pt, m); err != nil {
t.FailNow()
}
if pt.B.Value != newValue {
t.Fatalf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue)
}
}
}
type structWithTimePointer struct {
Birth *time.Time
}
func TestTime(t *testing.T) {
now := time.Now()
dataStruct := structWithTimePointer{
Birth: &now,
}
dataMap := map[string]interface{}{
"Birth": &now,
}
b := structWithTimePointer{}
if err := Merge(&b, dataStruct); err != nil {
t.FailNow()
}
if b.Birth.IsZero() {
t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
}
if b.Birth != dataStruct.Birth {
t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
}
b = structWithTimePointer{}
if err := Map(&b, dataMap); err != nil {
t.FailNow()
}
if b.Birth.IsZero() {
t.Fatalf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"])
}
}
type simpleNested struct {
A int
}
type structWithNestedPtrValueMap struct {
NestedPtrValue map[string]*simpleNested
}
func TestNestedPtrValueInMap(t *testing.T) {
src := &structWithNestedPtrValueMap{
NestedPtrValue: map[string]*simpleNested{
"x": {
A: 1,
},
},
}
dst := &structWithNestedPtrValueMap{
NestedPtrValue: map[string]*simpleNested{
"x": {},
},
}
if err := Map(dst, src); err != nil {
t.FailNow()
}
if dst.NestedPtrValue["x"].A == 0 {
t.Fatalf("Nested Ptr value not merged in properly: dst.NestedPtrValue[\"x\"].A(%v) != src.NestedPtrValue[\"x\"].A(%v)", dst.NestedPtrValue["x"].A, src.NestedPtrValue["x"].A)
}
}
func loadYAML(path string) (m map[string]interface{}) {
m = make(map[string]interface{})
raw, _ := ioutil.ReadFile(path)
_ = yaml.Unmarshal(raw, &m)
return
}
type structWithMap struct {
m map[string]structWithUnexportedProperty
}
type structWithUnexportedProperty struct {
s string
}
func TestUnexportedProperty(t *testing.T) {
a := structWithMap{map[string]structWithUnexportedProperty{
"key": structWithUnexportedProperty{"hello"},
}}
b := structWithMap{map[string]structWithUnexportedProperty{
"key": structWithUnexportedProperty{"hi"},
}}
defer func() {
if r := recover(); r != nil {
t.Errorf("Should not have panicked")
}
}()
Merge(&a, b)
}
type structWithBoolPointer struct {
C *bool
}
func TestBooleanPointer(t *testing.T) {
bt, bf := true, false
src := structWithBoolPointer{
&bt,
}
dst := structWithBoolPointer{
&bf,
}
if err := Merge(&dst, src); err != nil {
t.FailNow()
}
if dst.C == src.C {
t.Fatalf("dst.C should be a different pointer than src.C")
}
if *dst.C != *src.C {
t.Fatalf("dst.C should be true")
}
}

View File

@@ -1,6 +0,0 @@
fields:
id: int
name: string
parent: ref "datu:thing"
status: enum(draft, public, private)
author: updater