The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .deepsource.toml
├── .github
    ├── FUNDING.yml
    ├── dependabot.yml
    └── workflows
    │   ├── codeql-analysis.yml
    │   ├── linters.yml
    │   ├── scorecard.yml
    │   └── tests.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .travis.yml
├── .vscode
    └── settings.json
├── .well-known
    └── funding-manifest-urls
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── FUNDING.json
├── LICENSE
├── README.md
├── SECURITY.md
├── benchmark_test.go
├── doc.go
├── go.mod
├── go.sum
├── internal
    └── ng
    │   ├── README.md
    │   ├── merge.go
    │   └── merge_test.go
├── issue100_test.go
├── issue104_test.go
├── issue121_test.go
├── issue123_test.go
├── issue125_test.go
├── issue129_test.go
├── issue131_test.go
├── issue136_test.go
├── issue138_test.go
├── issue143_test.go
├── issue149_test.go
├── issue174_test.go
├── issue17_test.go
├── issue187_test.go
├── issue202_test.go
├── issue209_test.go
├── issue220_test.go
├── issue230_test.go
├── issue23_test.go
├── issue33_test.go
├── issue38_test.go
├── issue50_test.go
├── issue52_test.go
├── issue61_test.go
├── issue64_test.go
├── issue66_test.go
├── issue83_test.go
├── issue84_test.go
├── issue89_test.go
├── issue90_test.go
├── issueXXX_test.go
├── map.go
├── merge.go
├── merge_test.go
├── mergo.go
├── mergo_test.go
├── pr211_2_test.go
├── pr211_test.go
├── pr80_test.go
├── pr81_test.go
├── testdata
    ├── license.json
    └── thing.json
└── v039_bugs_test.go


/.deepsource.toml:
--------------------------------------------------------------------------------
 1 | version = 1
 2 | 
 3 | test_patterns = [
 4 |   "*_test.go"
 5 | ]
 6 | 
 7 | [[analyzers]]
 8 | name = "go"
 9 | enabled = true
10 | 
11 |   [analyzers.meta]
12 |   import_path = "dario.cat/mergo"


--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | 
3 | github: darccio
4 | tidelift: go/dario.cat/mergo
5 | thanks_dev: gh/darccio
6 | liberapay: dario
7 | 


--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
 1 | version: 2
 2 | updates:
 3 |   - package-ecosystem: gomod
 4 |     directory: /
 5 |     schedule:
 6 |       interval: weekly
 7 |   - package-ecosystem: github-actions
 8 |     directory: /
 9 |     schedule:
10 |       interval: daily
11 | 


--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
 1 | name: "CodeQL"
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [ master ]
 6 |   pull_request:
 7 |     # The branches below must be a subset of the branches above
 8 |     branches: [ master ]
 9 |   schedule:
10 |     - cron: '43 19 * * 2'
11 | 
12 | permissions:
13 |   contents: read
14 | 
15 | jobs:
16 |   analyze:
17 |     name: Analyze
18 |     runs-on: ubuntu-latest
19 |     permissions:
20 |       actions: read
21 |       contents: read
22 |       security-events: write
23 | 
24 |     strategy:
25 |       fail-fast: false
26 |       matrix:
27 |         language: [ 'go' ]
28 |         # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
29 |         # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
30 | 
31 |     steps:
32 |     - name: Checkout repository
33 |       uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
34 | 
35 |     # Initializes the CodeQL tools for scanning.
36 |     - name: Initialize CodeQL
37 |       uses: github/codeql-action/init@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
38 |       with:
39 |         languages: ${{ matrix.language }}
40 |         # If you wish to specify custom queries, you can do so here or in a config file.
41 |         # By default, queries listed here will override any specified in a config file.
42 |         # Prefix the list here with "+" to use these queries and those in the config file.
43 |         
44 |         # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
45 |         # queries: security-extended,security-and-quality
46 | 
47 |         
48 |     # Autobuild attempts to build any compiled languages  (C/C++, C#, or Java).
49 |     # If this step fails, then you should remove it and run the build manually (see below)
50 |     - name: Autobuild
51 |       uses: github/codeql-action/autobuild@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
52 | 
53 |     # ℹ️ Command-line programs to run using the OS shell.
54 |     # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
55 | 
56 |     #   If the Autobuild fails above, remove it and uncomment the following three lines. 
57 |     #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
58 | 
59 |     # - run: |
60 |     #   echo "Run, Build Application using script"
61 |     #   ./location_of_script_within_repo/buildscript.sh
62 | 
63 |     - name: Perform CodeQL Analysis
64 |       uses: github/codeql-action/analyze@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
65 | 


--------------------------------------------------------------------------------
/.github/workflows/linters.yml:
--------------------------------------------------------------------------------
 1 | name: 'linters'
 2 | on:
 3 |   push:
 4 |     branches:
 5 |       - master
 6 |   pull_request:
 7 |   workflow_dispatch:
 8 | permissions:
 9 |   contents: read
10 | jobs:
11 |   golangci-lint:
12 |     runs-on: ubuntu-latest
13 |     permissions:
14 |       contents: read # for actions/checkout to fetch code
15 |       pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
16 |     steps:
17 |       - name: Checkout code
18 |         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
19 |         with:
20 |           persist-credentials: false
21 |       - name: Setup Go
22 |         uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
23 |         with:
24 |           go-version: 'stable'
25 |       - name: golangci-lint
26 |         uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
27 |         with:
28 |           version: v2.1
29 | 


--------------------------------------------------------------------------------
/.github/workflows/scorecard.yml:
--------------------------------------------------------------------------------
 1 | # This workflow uses actions that are not certified by GitHub. They are provided
 2 | # by a third-party and are governed by separate terms of service, privacy
 3 | # policy, and support documentation.
 4 | 
 5 | name: Scorecard supply-chain security
 6 | on:
 7 |   # For Branch-Protection check. Only the default branch is supported. See
 8 |   # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
 9 |   branch_protection_rule:
10 |   # To guarantee Maintained check is occasionally updated. See
11 |   # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
12 |   schedule:
13 |     - cron: '24 16 * * 6'
14 |   push:
15 |     branches: [ "master" ]
16 |   workflow_dispatch:
17 | 
18 | # Declare default permissions as read only.
19 | permissions: read-all
20 | 
21 | jobs:
22 |   analysis:
23 |     name: Scorecard analysis
24 |     runs-on: ubuntu-latest
25 |     # `publish_results: true` only works when run from the default branch. conditional can be removed if disabled.
26 |     if: github.event.repository.default_branch == github.ref_name || github.event_name == 'pull_request'
27 |     permissions:
28 |       # Needed to upload the results to code-scanning dashboard.
29 |       security-events: write
30 |       # Needed to publish results and get a badge (see publish_results below).
31 |       id-token: write
32 |       # Uncomment the permissions below if installing in a private repository.
33 |       # contents: read
34 |       # actions: read
35 | 
36 |     steps:
37 |       - name: "Checkout code"
38 |         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
39 |         with:
40 |           persist-credentials: false
41 | 
42 |       - name: "Run analysis"
43 |         uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
44 |         with:
45 |           results_file: results.sarif
46 |           results_format: sarif
47 |           # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
48 |           # - you want to enable the Branch-Protection check on a *public* repository, or
49 |           # - you are installing Scorecard on a *private* repository
50 |           # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
51 |           # repo_token: ${{ secrets.SCORECARD_TOKEN }}
52 | 
53 |           # Public repositories:
54 |           #   - Publish results to OpenSSF REST API for easy access by consumers
55 |           #   - Allows the repository to include the Scorecard badge.
56 |           #   - See https://github.com/ossf/scorecard-action#publishing-results.
57 |           # For private repositories:
58 |           #   - `publish_results` will always be set to `false`, regardless
59 |           #     of the value entered here.
60 |           publish_results: true
61 | 
62 |           # (Optional) Uncomment file_mode if you have a .gitattributes with files marked export-ignore
63 |           # file_mode: git
64 | 
65 |       # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
66 |       # format to the repository Actions tab.
67 |       - name: "Upload artifact"
68 |         uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
69 |         with:
70 |           name: SARIF file
71 |           path: results.sarif
72 |           retention-days: 5
73 | 
74 |       # Upload the results to GitHub's code scanning dashboard (optional).
75 |       # Commenting out will disable upload of results to your repo's Code Scanning dashboard
76 |       - name: "Upload to code-scanning"
77 |         uses: github/codeql-action/upload-sarif@fca7ace96b7d713c7035871441bd52efbe39e27e # v3.28.19
78 |         with:
79 |           sarif_file: results.sarif
80 | 


--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
 1 | name: 'tests'
 2 | on:
 3 |   push:
 4 |     branches:
 5 |       - master
 6 |   pull_request:
 7 |   workflow_dispatch:
 8 | permissions:
 9 |   contents: read
10 | jobs:
11 |   build:
12 |     runs-on: ubuntu-latest
13 |     permissions:
14 |       checks: write # for coverallsapp/github-action to create new checks
15 |       contents: read # for actions/checkout to fetch code
16 |     steps:
17 |       - name: Checkout code
18 |         uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
19 |         with:
20 |           persist-credentials: false
21 |       - name: Setup Go
22 |         uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0
23 |         with:
24 |           go-version: 1.23.9
25 |       - name: Build
26 |         run: go build -v ./...
27 |       - name: Test
28 |         run: go test -v -race -covermode atomic -coverprofile=covprofile ./...
29 |       - name: Coverage
30 |         uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # v2.3.6
31 |         with:
32 |           file: covprofile
33 |           format: golang
34 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | #### joe made this: http://goel.io/joe
 2 | 
 3 | #### go ####
 4 | # Binaries for programs and plugins
 5 | *.exe
 6 | *.dll
 7 | *.so
 8 | *.dylib
 9 | 
10 | # Test binary, build with `go test -c`
11 | *.test
12 | 
13 | # Output of the go coverage tool, specifically when used with LiteIDE
14 | *.out
15 | 
16 | # Golang/Intellij
17 | .idea
18 | 
19 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736
20 | .glide/
21 | 
22 | #### vim ####
23 | # Swap
24 | [._]*.s[a-v][a-z]
25 | [._]*.sw[a-p]
26 | [._]s[a-v][a-z]
27 | [._]sw[a-p]
28 | 
29 | # Session
30 | Session.vim
31 | 
32 | # Temporary
33 | .netrwhist
34 | *~
35 | # Auto-generated tag files
36 | tags
37 | 


--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
 1 | repos:
 2 | - repo: https://github.com/gitleaks/gitleaks
 3 |   rev: v8.16.3
 4 |   hooks:
 5 |   - id: gitleaks
 6 | - repo: https://github.com/golangci/golangci-lint
 7 |   rev: v1.52.2
 8 |   hooks:
 9 |   - id: golangci-lint
10 | - repo: https://github.com/pre-commit/pre-commit-hooks
11 |   rev: v4.4.0
12 |   hooks:
13 |   - id: end-of-file-fixer
14 |   - id: trailing-whitespace
15 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | language: go
 2 | arch:
 3 |     - amd64
 4 |     - ppc64le
 5 | install:
 6 |   - go get -t
 7 |   - go get golang.org/x/tools/cmd/cover
 8 |   - go get github.com/mattn/goveralls
 9 | script:
10 |   - go test -race -v ./...
11 | after_script:
12 |   - $HOME/gopath/bin/goveralls -service=travis-ci -repotoken $COVERALLS_TOKEN
13 | 


--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 |     "go.lintTool": "golangci-lint",
3 |     "go.lintFlags": [
4 |         "--enable-all",
5 |         "--disable=gomnd"
6 |     ]
7 | }


--------------------------------------------------------------------------------
/.well-known/funding-manifest-urls:
--------------------------------------------------------------------------------
1 | https://dario.cat/funding.json


--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
 1 | # Contributor Covenant Code of Conduct
 2 | 
 3 | ## Our Pledge
 4 | 
 5 | 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.
 6 | 
 7 | ## Our Standards
 8 | 
 9 | Examples of behavior that contributes to creating a positive environment include:
10 | 
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 | 
17 | Examples of unacceptable behavior by participants include:
18 | 
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 | 
25 | ## Our Responsibilities
26 | 
27 | 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.
28 | 
29 | 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.
30 | 
31 | ## Scope
32 | 
33 | 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.
34 | 
35 | ## Enforcement
36 | 
37 | 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.
38 | 
39 | 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.
40 | 
41 | ## Attribution
42 | 
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 | 
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 | 


--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
  1 | <!-- omit in toc -->
  2 | # Contributing to mergo
  3 | 
  4 | First off, thanks for taking the time to contribute! ❤️
  5 | 
  6 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉
  7 | 
  8 | > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about:
  9 | > - Star the project
 10 | > - Tweet about it
 11 | > - Refer this project in your project's readme
 12 | > - Mention the project at local meetups and tell your friends/colleagues
 13 | 
 14 | <!-- omit in toc -->
 15 | ## Table of Contents
 16 | 
 17 | - [Code of Conduct](#code-of-conduct)
 18 | - [I Have a Question](#i-have-a-question)
 19 | - [I Want To Contribute](#i-want-to-contribute)
 20 | - [Reporting Bugs](#reporting-bugs)
 21 | - [Suggesting Enhancements](#suggesting-enhancements)
 22 | 
 23 | ## Code of Conduct
 24 | 
 25 | This project and everyone participating in it is governed by the
 26 | [mergo Code of Conduct](https://github.com/darccio/mergo/blob/master/CODE_OF_CONDUCT.md).
 27 | By participating, you are expected to uphold this code. Please report unacceptable behavior
 28 | to <>.
 29 | 
 30 | 
 31 | ## I Have a Question
 32 | 
 33 | > If you want to ask a question, we assume that you have read the available [Documentation](https://pkg.go.dev/github.com/darccio/mergo).
 34 | 
 35 | Before you ask a question, it is best to search for existing [Issues](https://github.com/darccio/mergo/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first.
 36 | 
 37 | If you then still feel the need to ask a question and need clarification, we recommend the following:
 38 | 
 39 | - Open an [Issue](https://github.com/darccio/mergo/issues/new).
 40 | - Provide as much context as you can about what you're running into.
 41 | - Provide project and platform versions (nodejs, npm, etc), depending on what seems relevant.
 42 | 
 43 | We will then take care of the issue as soon as possible.
 44 | 
 45 | ## I Want To Contribute
 46 | 
 47 | > ### Legal Notice <!-- omit in toc -->
 48 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license.
 49 | 
 50 | ### Reporting Bugs
 51 | 
 52 | <!-- omit in toc -->
 53 | #### Before Submitting a Bug Report
 54 | 
 55 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible.
 56 | 
 57 | - Make sure that you are using the latest version.
 58 | - Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](). If you are looking for support, you might want to check [this section](#i-have-a-question)).
 59 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/darccio/mergo/issues?q=label%3Abug).
 60 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue.
 61 | - Collect information about the bug:
 62 | - Stack trace (Traceback)
 63 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM)
 64 | - Version of the interpreter, compiler, SDK, runtime environment, package manager, depending on what seems relevant.
 65 | - Possibly your input and the output
 66 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions?
 67 | 
 68 | <!-- omit in toc -->
 69 | #### How Do I Submit a Good Bug Report?
 70 | 
 71 | > You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to .
 72 | <!-- You may add a PGP key to allow the messages to be sent encrypted as well. -->
 73 | 
 74 | We use GitHub issues to track bugs and errors. If you run into an issue with the project:
 75 | 
 76 | - Open an [Issue](https://github.com/darccio/mergo/issues/new). (Since we can't be sure at this point whether it is a bug or not, we ask you not to talk about a bug yet and not to label the issue.)
 77 | - Explain the behavior you would expect and the actual behavior.
 78 | - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case.
 79 | - Provide the information you collected in the previous section.
 80 | 
 81 | Once it's filed:
 82 | 
 83 | - The project team will label the issue accordingly.
 84 | - A team member will try to reproduce the issue with your provided steps. If there are no reproduction steps or no obvious way to reproduce the issue, the team will ask you for those steps and mark the issue as `needs-repro`. Bugs with the `needs-repro` tag will not be addressed until they are reproduced.
 85 | - If the team is able to reproduce the issue, it will be marked `needs-fix`, as well as possibly other tags (such as `critical`), and the issue will be left to be implemented by someone.
 86 | 
 87 | ### Suggesting Enhancements
 88 | 
 89 | This section guides you through submitting an enhancement suggestion for mergo, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions.
 90 | 
 91 | <!-- omit in toc -->
 92 | #### Before Submitting an Enhancement
 93 | 
 94 | - Make sure that you are using the latest version.
 95 | - Read the [documentation]() carefully and find out if the functionality is already covered, maybe by an individual configuration.
 96 | - Perform a [search](https://github.com/darccio/mergo/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
 97 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library.
 98 | 
 99 | <!-- omit in toc -->
100 | #### How Do I Submit a Good Enhancement Suggestion?
101 | 
102 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/darccio/mergo/issues).
103 | 
104 | - Use a **clear and descriptive title** for the issue to identify the suggestion.
105 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible.
106 | - **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you.
107 | - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://www.cockos.com/licecap/) to record GIFs on macOS and Windows, and [this tool](https://github.com/colinkeenan/silentcast) or [this tool](https://github.com/GNOME/byzanz) on Linux. <!-- this should only be included if the project has a GUI -->
108 | - **Explain why this enhancement would be useful** to most mergo users. You may also want to point out the other projects that solved it better and which could serve as inspiration.
109 | 
110 | <!-- omit in toc -->
111 | ## Attribution
112 | This guide is based on the **contributing-gen**. [Make your own](https://github.com/bttger/contributing-gen)!
113 | 


--------------------------------------------------------------------------------
/FUNDING.json:
--------------------------------------------------------------------------------
1 | {
2 |   "drips": {
3 |     "ethereum": {
4 |       "ownedBy": "0x6160020e7102237aC41bdb156e94401692D76930"
5 |     }
6 |   }
7 | }
8 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright (c) 2013 Dario Castañé. All rights reserved.
 2 | Copyright (c) 2012 The Go Authors. All rights reserved.
 3 | 
 4 | Redistribution and use in source and binary forms, with or without
 5 | modification, are permitted provided that the following conditions are
 6 | met:
 7 | 
 8 |    * Redistributions of source code must retain the above copyright
 9 | notice, this list of conditions and the following disclaimer.
10 |    * Redistributions in binary form must reproduce the above
11 | copyright notice, this list of conditions and the following disclaimer
12 | in the documentation and/or other materials provided with the
13 | distribution.
14 |    * Neither the name of Google Inc. nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 | 
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
  1 | # Mergo
  2 | 
  3 | [![GitHub release][5]][6]
  4 | [![GoCard][7]][8]
  5 | [![Test status][1]][2]
  6 | [![OpenSSF Scorecard][21]][22]
  7 | [![OpenSSF Best Practices][19]][20]
  8 | [![Coverage status][9]][10]
  9 | [![Sourcegraph][11]][12]
 10 | [![FOSSA status][13]][14]
 11 | 
 12 | [![GoDoc][3]][4]
 13 | [![Become my sponsor][15]][16]
 14 | [![Tidelift][17]][18]
 15 | [![Liberapay patrons][23]][24]
 16 | [![Support mergo on drips.network][25]][26]
 17 | 
 18 | [1]: https://github.com/darccio/mergo/workflows/tests/badge.svg?branch=master
 19 | [2]: https://github.com/darccio/mergo/actions/workflows/tests.yml
 20 | [3]: https://pkg.go.dev/badge/dario.cat/mergo
 21 | [4]: https://pkg.go.dev/dario.cat/mergo
 22 | [5]: https://img.shields.io/github/release/darccio/mergo.svg
 23 | [6]: https://github.com/darccio/mergo/releases
 24 | [7]: https://goreportcard.com/badge/dario.cat/mergo
 25 | [8]: https://goreportcard.com/report/dario.cat/mergo
 26 | [9]: https://coveralls.io/repos/github/darccio/mergo/badge.svg?branch=master
 27 | [10]: https://coveralls.io/github/darccio/mergo?branch=master
 28 | [11]: https://sourcegraph.com/github.com/imdario/mergo/-/badge.svg
 29 | [12]: https://sourcegraph.com/github.com/imdario/mergo?badge
 30 | [13]: https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=shield
 31 | [14]: https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_shield
 32 | [15]: https://img.shields.io/github/sponsors/darccio
 33 | [16]: https://github.com/sponsors/darccio
 34 | [17]: https://tidelift.com/badges/package/go/dario.cat%2Fmergo
 35 | [18]: https://tidelift.com/subscription/pkg/go-dario.cat-mergo
 36 | [19]: https://www.bestpractices.dev/projects/7177/badge
 37 | [20]: https://www.bestpractices.dev/en/projects/7177
 38 | [21]: https://api.scorecard.dev/projects/github.com/darccio/mergo/badge
 39 | [22]: https://scorecard.dev/viewer/?uri=github.com/darccio/mergo
 40 | [23]: https://img.shields.io/liberapay/patrons/dario
 41 | [24]: https://liberapay.com/dario/donate
 42 | [25]: https://www.drips.network/api/embed/project/https%3A%2F%2Fgithub.com%2Fdarccio%2Fmergo/support.png?background=light&style=github&text=project&stat=none
 43 | [26]: https://www.drips.network/app/projects/github/darccio/mergo
 44 | 
 45 | A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
 46 | 
 47 | Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
 48 | 
 49 | Also a lovely [comune](http://en.wikipedia.org/wiki/Mergo) (municipality) in the Province of Ancona in the Italian region of Marche.
 50 | 
 51 | ## Status
 52 | 
 53 | Mergo is stable and frozen, ready for production. Check a short list of the projects using at large scale it [here](https://github.com/darccio/mergo#mergo-in-the-wild).
 54 | 
 55 | No new features are accepted. They will be considered for a future v2 that improves the implementation and fixes bugs for corner cases.
 56 | 
 57 | ### Important notes
 58 | 
 59 | #### 1.0.0
 60 | 
 61 | In [1.0.0](//github.com/darccio/mergo/releases/tag/1.0.0) Mergo moves to a vanity URL `dario.cat/mergo`. No more v1 versions will be released.
 62 | 
 63 | If the vanity URL is causing issues in your project due to a dependency pulling Mergo - it isn't a direct dependency in your project - it is recommended to use [replace](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) to pin the version to the last one with the old import URL:
 64 | 
 65 | ```
 66 | replace github.com/imdario/mergo => github.com/imdario/mergo v0.3.16
 67 | ```
 68 | 
 69 | #### 0.3.9
 70 | 
 71 | Please keep in mind that a problematic PR broke [0.3.9](//github.com/darccio/mergo/releases/tag/0.3.9). I reverted it in [0.3.10](//github.com/darccio/mergo/releases/tag/0.3.10), and I consider it stable but not bug-free. Also, this version adds support for go modules.
 72 | 
 73 | Keep in mind that in [0.3.2](//github.com/darccio/mergo/releases/tag/0.3.2), Mergo changed `Merge()`and `Map()` signatures to support [transformers](#transformers). I added an optional/variadic argument so that it won't break the existing code.
 74 | 
 75 | 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 dario.cat/mergo```. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
 76 | 
 77 | ### Donations
 78 | 
 79 | If Mergo is useful to you, consider buying me a coffee, a beer, or making a monthly donation to allow me to keep building great free software. :heart_eyes:
 80 | 
 81 | <a href="https://liberapay.com/dario/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a>
 82 | <a href='https://github.com/sponsors/darccio' target='_blank'><img alt="Become my sponsor" src="https://img.shields.io/github/sponsors/darccio?style=for-the-badge" /></a>
 83 | 
 84 | ### Mergo in the wild
 85 | 
 86 | Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/dependents) [of](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.16/dependents) [projects](https://deps.dev/go/github.com%2Fimdario%2Fmergo/v0.3.12), including:
 87 | 
 88 | * [containerd/containerd](https://github.com/containerd/containerd)
 89 | * [datadog/datadog-agent](https://github.com/datadog/datadog-agent)
 90 | * [docker/cli/](https://github.com/docker/cli/)
 91 | * [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
 92 | * [go-micro/go-micro](https://github.com/go-micro/go-micro)
 93 | * [grafana/loki](https://github.com/grafana/loki)
 94 | * [masterminds/sprig](github.com/Masterminds/sprig)
 95 | * [moby/moby](https://github.com/moby/moby)
 96 | * [slackhq/nebula](https://github.com/slackhq/nebula)
 97 | * [volcano-sh/volcano](https://github.com/volcano-sh/volcano)
 98 | 
 99 | ## Install
100 | 
101 |     go get dario.cat/mergo
102 | 
103 |     // use in your .go code
104 |     import (
105 |         "dario.cat/mergo"
106 |     )
107 | 
108 | ## Usage
109 | 
110 | 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 zero values](https://golang.org/ref/spec#The_zero_value) too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
111 | 
112 | ```go
113 | if err := mergo.Merge(&dst, src); err != nil {
114 |     // ...
115 | }
116 | ```
117 | 
118 | Also, you can merge overwriting values using the transformer `WithOverride`.
119 | 
120 | ```go
121 | if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
122 |     // ...
123 | }
124 | ```
125 | 
126 | If you need to override pointers, so the source pointer's value is assigned to the destination's pointer, you must use `WithoutDereference`:
127 | 
128 | ```go
129 | package main
130 | 
131 | import (
132 | 	"fmt"
133 | 
134 | 	"dario.cat/mergo"
135 | )
136 | 
137 | type Foo struct {
138 | 	A *string
139 | 	B int64
140 | }
141 | 
142 | func main() {
143 | 	first := "first"
144 | 	second := "second"
145 | 	src := Foo{
146 | 		A: &first,
147 | 		B: 2,
148 | 	}
149 | 
150 | 	dest := Foo{
151 | 		A: &second,
152 | 		B: 1,
153 | 	}
154 | 
155 | 	mergo.Merge(&dest, src, mergo.WithOverride, mergo.WithoutDereference)
156 | }
157 | ```
158 | 
159 | 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.
160 | 
161 | ```go
162 | if err := mergo.Map(&dst, srcMap); err != nil {
163 |     // ...
164 | }
165 | ```
166 | 
167 | 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.
168 | 
169 | Here is a nice example:
170 | 
171 | ```go
172 | package main
173 | 
174 | import (
175 | 	"fmt"
176 | 	"dario.cat/mergo"
177 | )
178 | 
179 | type Foo struct {
180 | 	A string
181 | 	B int64
182 | }
183 | 
184 | func main() {
185 | 	src := Foo{
186 | 		A: "one",
187 | 		B: 2,
188 | 	}
189 | 	dest := Foo{
190 | 		A: "two",
191 | 	}
192 | 	mergo.Merge(&dest, src)
193 | 	fmt.Println(dest)
194 | 	// Will print
195 | 	// {two 2}
196 | }
197 | ```
198 | 
199 | ### Transformers
200 | 
201 | Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?
202 | 
203 | ```go
204 | package main
205 | 
206 | import (
207 | 	"fmt"
208 | 	"dario.cat/mergo"
209 |     "reflect"
210 |     "time"
211 | )
212 | 
213 | type timeTransformer struct {
214 | }
215 | 
216 | func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
217 | 	if typ == reflect.TypeOf(time.Time{}) {
218 | 		return func(dst, src reflect.Value) error {
219 | 			if dst.CanSet() {
220 | 				isZero := dst.MethodByName("IsZero")
221 | 				result := isZero.Call([]reflect.Value{})
222 | 				if result[0].Bool() {
223 | 					dst.Set(src)
224 | 				}
225 | 			}
226 | 			return nil
227 | 		}
228 | 	}
229 | 	return nil
230 | }
231 | 
232 | type Snapshot struct {
233 | 	Time time.Time
234 | 	// ...
235 | }
236 | 
237 | func main() {
238 | 	src := Snapshot{time.Now()}
239 | 	dest := Snapshot{}
240 | 	mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{}))
241 | 	fmt.Println(dest)
242 | 	// Will print
243 | 	// { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
244 | }
245 | ```
246 | 
247 | ## Contact me
248 | 
249 | 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)
250 | 
251 | ## About
252 | 
253 | Written by [Dario Castañé](http://dario.cat).
254 | 
255 | ## License
256 | 
257 | [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) license, as [Go language](http://golang.org/LICENSE).
258 | 
259 | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fimdario%2Fmergo.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fimdario%2Fmergo?ref=badge_large)
260 | 


--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
 1 | # Security Policy
 2 | 
 3 | ## Supported Versions
 4 | 
 5 | | Version | Supported          |
 6 | | ------- | ------------------ |
 7 | | 1.x.x   | :white_check_mark: |
 8 | | < 1.0   | :x:                |
 9 | 
10 | ## Security contact information
11 | 
12 | To report a security vulnerability, please use the
13 | [Tidelift security contact](https://tidelift.com/security).
14 | Tidelift will coordinate the fix and disclosure.
15 | 


--------------------------------------------------------------------------------
/benchmark_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func BenchmarkMerge(b *testing.B) {
10 | 	type ts struct {
11 | 		Field string
12 | 	}
13 | 
14 | 	var (
15 | 		dst = ts{}
16 | 		src = ts{
17 | 			Field: "src",
18 | 		}
19 | 	)
20 | 
21 | 	b.ResetTimer()
22 | 
23 | 	for i := 0; i < b.N; i++ {
24 | 		err := mergo.Merge(&dst, src)
25 | 		if err != nil {
26 | 			b.Fatal(err)
27 | 		}
28 | 		dst.Field = ""
29 | 	}
30 | }
31 | 


--------------------------------------------------------------------------------
/doc.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2013 Dario Castañé. All rights reserved.
  2 | // Copyright 2009 The Go Authors. All rights reserved.
  3 | // Use of this source code is governed by a BSD-style
  4 | // license that can be found in the LICENSE file.
  5 | 
  6 | /*
  7 | A helper to merge structs and maps in Golang. Useful for configuration default values, avoiding messy if-statements.
  8 | 
  9 | Mergo merges same-type structs and maps by setting default values in zero-value fields. Mergo won't merge unexported (private) fields. It will do recursively any exported one. It also won't merge structs inside maps (because they are not addressable using Go reflection).
 10 | 
 11 | # Status
 12 | 
 13 | It is ready for production use. It is used in several projects by Docker, Google, The Linux Foundation, VMWare, Shopify, etc.
 14 | 
 15 | # Important notes
 16 | 
 17 | 1.0.0
 18 | 
 19 | In 1.0.0 Mergo moves to a vanity URL `dario.cat/mergo`.
 20 | 
 21 | 0.3.9
 22 | 
 23 | Please keep in mind that a problematic PR broke 0.3.9. We reverted it in 0.3.10. We consider 0.3.10 as stable but not bug-free. . Also, this version adds suppot for go modules.
 24 | 
 25 | Keep in mind that in 0.3.2, Mergo changed Merge() and Map() signatures to support transformers. We added an optional/variadic argument so that it won't break the existing code.
 26 | 
 27 | 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 dario.cat/mergo. I apologize for any issue caused by its previous behavior and any future bug that Mergo could cause in existing projects after the change (release 0.2.0).
 28 | 
 29 | # Install
 30 | 
 31 | Do your usual installation procedure:
 32 | 
 33 | 	go get dario.cat/mergo
 34 | 
 35 | 	// use in your .go code
 36 | 	import (
 37 | 	    "dario.cat/mergo"
 38 | 	)
 39 | 
 40 | # Usage
 41 | 
 42 | 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 zero values too. Also, maps will be merged recursively except for structs inside maps (because they are not addressable using Go reflection).
 43 | 
 44 | 	if err := mergo.Merge(&dst, src); err != nil {
 45 | 		// ...
 46 | 	}
 47 | 
 48 | Also, you can merge overwriting values using the transformer WithOverride.
 49 | 
 50 | 	if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
 51 | 		// ...
 52 | 	}
 53 | 
 54 | 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.
 55 | 
 56 | 	if err := mergo.Map(&dst, srcMap); err != nil {
 57 | 		// ...
 58 | 	}
 59 | 
 60 | 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.
 61 | 
 62 | Here is a nice example:
 63 | 
 64 | 	package main
 65 | 
 66 | 	import (
 67 | 		"fmt"
 68 | 		"dario.cat/mergo"
 69 | 	)
 70 | 
 71 | 	type Foo struct {
 72 | 		A string
 73 | 		B int64
 74 | 	}
 75 | 
 76 | 	func main() {
 77 | 		src := Foo{
 78 | 			A: "one",
 79 | 			B: 2,
 80 | 		}
 81 | 		dest := Foo{
 82 | 			A: "two",
 83 | 		}
 84 | 		mergo.Merge(&dest, src)
 85 | 		fmt.Println(dest)
 86 | 		// Will print
 87 | 		// {two 2}
 88 | 	}
 89 | 
 90 | # Transformers
 91 | 
 92 | Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, time.Time is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero time.Time?
 93 | 
 94 | 	package main
 95 | 
 96 | 	import (
 97 | 		"fmt"
 98 | 		"dario.cat/mergo"
 99 | 		"reflect"
100 | 		"time"
101 | 	)
102 | 
103 | 	type timeTransformer struct {
104 | 	}
105 | 
106 | 	func (t timeTransformer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
107 | 		if typ == reflect.TypeOf(time.Time{}) {
108 | 			return func(dst, src reflect.Value) error {
109 | 				if dst.CanSet() {
110 | 					isZero := dst.MethodByName("IsZero")
111 | 					result := isZero.Call([]reflect.Value{})
112 | 					if result[0].Bool() {
113 | 						dst.Set(src)
114 | 					}
115 | 				}
116 | 				return nil
117 | 			}
118 | 		}
119 | 		return nil
120 | 	}
121 | 
122 | 	type Snapshot struct {
123 | 		Time time.Time
124 | 		// ...
125 | 	}
126 | 
127 | 	func main() {
128 | 		src := Snapshot{time.Now()}
129 | 		dest := Snapshot{}
130 | 		mergo.Merge(&dest, src, mergo.WithTransformers(timeTransformer{}))
131 | 		fmt.Println(dest)
132 | 		// Will print
133 | 		// { 2018-01-12 01:15:00 +0000 UTC m=+0.000000001 }
134 | 	}
135 | 
136 | # Contact me
137 | 
138 | 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): https://twitter.com/im_dario
139 | 
140 | # About
141 | 
142 | Written by Dario Castañé: https://da.rio.hn
143 | 
144 | # License
145 | 
146 | BSD 3-Clause license, as Go language.
147 | */
148 | package mergo
149 | 


--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module dario.cat/mergo
2 | 
3 | go 1.18
4 | 


--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/darccio/mergo/9b031a0e7dd017cda99cf4d5f0ee18aa24252367/go.sum


--------------------------------------------------------------------------------
/internal/ng/README.md:
--------------------------------------------------------------------------------
 1 | # Mergo
 2 | 
 3 | ## Why next gen?
 4 | 
 5 | 1. Cleaner code.
 6 | 2. Reduce `interface{}`/`any` usage in the API.
 7 | 3. Allow the compiler to optimize the code through generics.
 8 | 4. Reduce allocations: v1 does 4 allocations per merge.
 9 | 5. Reduce `reflect` usage.
10 | 6. Migrate from sentinel errors to [concrete error types](https://jub0bs.com/posts/2025-03-31-why-concrete-error-types-are-superior-to-sentinel-errors/).
11 | 


--------------------------------------------------------------------------------
/internal/ng/merge.go:
--------------------------------------------------------------------------------
  1 | package ng
  2 | 
  3 | import "reflect"
  4 | 
  5 | // Copyright 2025 Dario Castañé. All rights reserved.
  6 | // Copyright 2009 The Go Authors. All rights reserved.
  7 | // Use of this source code is governed by a BSD-style
  8 | // license that can be found in the LICENSE file.
  9 | 
 10 | // Based on src/reflect/deepequal.go from official
 11 | // golang's stdlib.
 12 | 
 13 | // Don't use this package. It's a work in progress.
 14 | // This is the next generation of mergo, and it's not ready for production.
 15 | 
 16 | type NilArgumentsError struct{}
 17 | 
 18 | func (*NilArgumentsError) Error() string {
 19 | 	return "src and dst must not be nil"
 20 | }
 21 | 
 22 | type InvalidDestinationError struct{}
 23 | 
 24 | func (*InvalidDestinationError) Error() string {
 25 | 	return "dst must be a pointer"
 26 | }
 27 | 
 28 | type DifferentArgumentTypesError struct{}
 29 | 
 30 | func (*DifferentArgumentTypesError) Error() string {
 31 | 	return "dst and src must have the same type"
 32 | }
 33 | 
 34 | // Merge sets any [zero-value](https://go.dev/ref/spec#The_zero_value) field
 35 | // in dst with the same field's value in src.
 36 | // Both dst and src must have the same type, being dst a pointer to the type.
 37 | // Merge returns NilArgumentsError if dst is a nil pointer and/or src is a nil
 38 | // value.
 39 | // If dst and src are values of predefined types, and dst is the type's zero
 40 | // value, src is assigned to dst.
 41 | // Merge is a convenient wrapper around the more compiler-friendly MergeValue
 42 | // and MergePtr functions.
 43 | func Merge(dst, src any) error {
 44 | 	if dst == nil {
 45 | 		// As dst pointer is a copy; assigning src to a nil pointer is an
 46 | 		// ineffective assignment.
 47 | 		return new(NilArgumentsError)
 48 | 	}
 49 | 
 50 | 	if src == nil {
 51 | 		// Nothing to do here.
 52 | 		return new(NilArgumentsError)
 53 | 	}
 54 | 
 55 | 	dstValue := reflect.ValueOf(dst)
 56 | 	if dstValue.Kind() != reflect.Ptr {
 57 | 		return new(InvalidDestinationError)
 58 | 	}
 59 | 
 60 | 	dstValue = dstValue.Elem()
 61 | 	srcValue := reflect.ValueOf(src)
 62 | 
 63 | 	if srcValue.Kind() == reflect.Ptr {
 64 | 		srcValue = srcValue.Elem()
 65 | 	}
 66 | 
 67 | 	if dstValue.Type() != srcValue.Type() {
 68 | 		return new(DifferentArgumentTypesError)
 69 | 	}
 70 | 
 71 | 	merge(dstValue, srcValue, dstValue.Type())
 72 | 
 73 | 	return nil
 74 | }
 75 | 
 76 | func MergeValue[T any](dst *T, src T) error {
 77 | 	if dst == nil {
 78 | 		// As dst pointer is a copy; assigning src to a nil pointer is an
 79 | 		// ineffective assignment.
 80 | 		return new(NilArgumentsError)
 81 | 	}
 82 | 
 83 | 	dstValue := reflect.ValueOf(dst).Elem()
 84 | 	srcValue := reflect.ValueOf(src)
 85 | 
 86 | 	merge(dstValue, srcValue, dstValue.Type())
 87 | 
 88 | 	return nil
 89 | }
 90 | 
 91 | func MergePtr[T any](dst, src *T) error {
 92 | 	if dst == nil {
 93 | 		// As dst pointer is a copy; assigning src to a nil pointer is an
 94 | 		// ineffective assignment.
 95 | 		return new(NilArgumentsError)
 96 | 	}
 97 | 
 98 | 	if src == nil {
 99 | 		// Nothing to do here.
100 | 		return new(NilArgumentsError)
101 | 	}
102 | 
103 | 	dstValue := reflect.ValueOf(dst).Elem()
104 | 	srcValue := reflect.ValueOf(src).Elem()
105 | 
106 | 	merge(dstValue, srcValue, dstValue.Type())
107 | 
108 | 	return nil
109 | }
110 | 
111 | func merge(dst, src reflect.Value, typ reflect.Type) {
112 | 	if typ.Kind() == reflect.Struct {
113 | 		mergeStruct(dst, src, typ)
114 | 	}
115 | 
116 | 	// TODO: handle maps and slices
117 | 	// TODO: handle pointers and interfaces
118 | 	// TODO: cover all potential empty cases (as in isEmptyValue from v1)
119 | 	if !dst.IsZero() {
120 | 		return
121 | 	}
122 | 
123 | 	dst.Set(src)
124 | }
125 | 
126 | func mergeStruct(dst, src reflect.Value, typ reflect.Type) {
127 | 	for i := 0; i < typ.NumField(); i++ {
128 | 		field := typ.Field(i)
129 | 		dstField := dst.Field(i)
130 | 		srcField := src.Field(i)
131 | 
132 | 		if !dstField.CanSet() {
133 | 			continue
134 | 		}
135 | 
136 | 		merge(dstField, srcField, field.Type)
137 | 	}
138 | }
139 | 


--------------------------------------------------------------------------------
/internal/ng/merge_test.go:
--------------------------------------------------------------------------------
  1 | package ng_test
  2 | 
  3 | import (
  4 | 	"errors"
  5 | 	"testing"
  6 | 
  7 | 	mergo "dario.cat/mergo/internal/ng"
  8 | )
  9 | 
 10 | func TestMerge(t *testing.T) {
 11 | 	t.Parallel()
 12 | 
 13 | 	type ts struct {
 14 | 		Field int
 15 | 	}
 16 | 
 17 | 	t.Run("merge", func(t *testing.T) {
 18 | 		t.Parallel()
 19 | 
 20 | 		var (
 21 | 			dst = ts{
 22 | 				Field: 0,
 23 | 			}
 24 | 			src = ts{
 25 | 				Field: 1,
 26 | 			}
 27 | 		)
 28 | 
 29 | 		if err := mergo.Merge(&dst, src); err != nil {
 30 | 			t.Errorf("Error while merging %s", err)
 31 | 		}
 32 | 
 33 | 		if dst.Field != src.Field {
 34 | 			t.Errorf("Expected dst.Field to be %d, got %d", src.Field, dst.Field)
 35 | 		}
 36 | 	})
 37 | 
 38 | 	t.Run("merge from pointer", func(t *testing.T) {
 39 | 		t.Parallel()
 40 | 
 41 | 		var (
 42 | 			dst = &ts{
 43 | 				Field: 0,
 44 | 			}
 45 | 			src = &ts{
 46 | 				Field: 1,
 47 | 			}
 48 | 		)
 49 | 
 50 | 		if err := mergo.Merge(dst, src); err != nil {
 51 | 			t.Errorf("Error while merging %s", err)
 52 | 		}
 53 | 
 54 | 		if dst.Field != src.Field {
 55 | 			t.Errorf("Expected dst.Field to be %d, got %d", src.Field, dst.Field)
 56 | 		}
 57 | 	})
 58 | }
 59 | 
 60 | func TestMergePredefinedType(t *testing.T) {
 61 | 	t.Parallel()
 62 | 
 63 | 	t.Run("merge ints", func(t *testing.T) {
 64 | 		t.Parallel()
 65 | 
 66 | 		var (
 67 | 			dst = 0
 68 | 			src = 1
 69 | 		)
 70 | 
 71 | 		if err := mergo.Merge(&dst, src); err != nil {
 72 | 			t.Errorf("Error while merging %s", err)
 73 | 		}
 74 | 
 75 | 		if dst != src {
 76 | 			t.Errorf("Expected dst to be %d, got %d", src, dst)
 77 | 		}
 78 | 	})
 79 | 
 80 | 	t.Run("merge strings", func(t *testing.T) {
 81 | 		t.Parallel()
 82 | 
 83 | 		var (
 84 | 			dst = ""
 85 | 			src = "src"
 86 | 		)
 87 | 
 88 | 		if err := mergo.Merge(&dst, src); err != nil {
 89 | 			t.Errorf("Error while merging %s", err)
 90 | 		}
 91 | 
 92 | 		if dst != src {
 93 | 			t.Errorf("Expected dst to be %q, got %q", src, dst)
 94 | 		}
 95 | 	})
 96 | }
 97 | 
 98 | func TestMergeNil(t *testing.T) {
 99 | 	t.Parallel()
100 | 
101 | 	t.Run("both nil", func(t *testing.T) {
102 | 		t.Parallel()
103 | 
104 | 		var naerr *mergo.NilArgumentsError
105 | 
106 | 		if err := mergo.Merge(nil, nil); !errors.As(err, &naerr) {
107 | 			t.Error(err)
108 | 		}
109 | 	})
110 | 
111 | 	t.Run("dst nil", func(t *testing.T) {
112 | 		t.Parallel()
113 | 
114 | 		var naerr *mergo.NilArgumentsError
115 | 
116 | 		if err := mergo.Merge(nil, struct{}{}); !errors.As(err, &naerr) {
117 | 			t.Error(err)
118 | 		}
119 | 	})
120 | 
121 | 	t.Run("src nil", func(t *testing.T) {
122 | 		t.Parallel()
123 | 
124 | 		var (
125 | 			dst   = struct{}{}
126 | 			naerr *mergo.NilArgumentsError
127 | 		)
128 | 
129 | 		if err := mergo.Merge(&dst, nil); !errors.As(err, &naerr) {
130 | 			t.Error(err)
131 | 		}
132 | 	})
133 | }
134 | 
135 | func TestMergeNotZero(t *testing.T) {
136 | 	t.Parallel()
137 | 
138 | 	type ts struct {
139 | 		Field int
140 | 	}
141 | 
142 | 	var (
143 | 		dst = ts{
144 | 			Field: 1,
145 | 		}
146 | 		src = ts{
147 | 			Field: 2,
148 | 		}
149 | 	)
150 | 
151 | 	if err := mergo.Merge(&dst, src); err != nil {
152 | 		t.Errorf("Error while merging %s", err)
153 | 	}
154 | 
155 | 	if dst.Field == src.Field {
156 | 		t.Errorf("Expected dst.Field to be 1, got %d", dst.Field)
157 | 	}
158 | }
159 | 
160 | func TestMergeOnlyExportedFields(t *testing.T) {
161 | 	t.Parallel()
162 | 
163 | 	type ts struct {
164 | 		Field      int
165 | 		unexported int
166 | 	}
167 | 
168 | 	var (
169 | 		dst = ts{
170 | 			Field:      0,
171 | 			unexported: 2,
172 | 		}
173 | 		src = ts{
174 | 			Field:      3,
175 | 			unexported: 4,
176 | 		}
177 | 	)
178 | 
179 | 	if err := mergo.Merge(&dst, src); err != nil {
180 | 		t.Errorf("Error while merging %s", err)
181 | 	}
182 | 
183 | 	if dst.Field != src.Field {
184 | 		t.Errorf("Expected dst.Field to be %d, got %d", src.Field, dst.Field)
185 | 	}
186 | 
187 | 	if dst.unexported == src.unexported {
188 | 		t.Errorf("Expected dst.unexported to be 2, got %d", dst.unexported)
189 | 	}
190 | }
191 | 
192 | func TestMergeNotPointer(t *testing.T) {
193 | 	t.Parallel()
194 | 
195 | 	var npeerr *mergo.InvalidDestinationError
196 | 	if err := mergo.Merge(struct{}{}, struct{}{}); !errors.As(err, &npeerr) {
197 | 		t.Error(err)
198 | 	}
199 | }
200 | 
201 | func TestMergeDifferentTypes(t *testing.T) {
202 | 	t.Parallel()
203 | 
204 | 	dst := struct {
205 | 		Field int
206 | 	}{
207 | 		Field: 1,
208 | 	}
209 | 
210 | 	src := struct {
211 | 		Field string
212 | 	}{
213 | 		Field: "src",
214 | 	}
215 | 
216 | 	var dteerr *mergo.DifferentArgumentTypesError
217 | 	if err := mergo.Merge(&dst, src); !errors.As(err, &dteerr) {
218 | 		t.Error(err)
219 | 	}
220 | }
221 | 
222 | func TestMergeValue(t *testing.T) {
223 | 	t.Parallel()
224 | 
225 | 	type ts struct {
226 | 		Field int
227 | 	}
228 | 
229 | 	dst := ts{
230 | 		Field: 0,
231 | 	}
232 | 
233 | 	src := ts{
234 | 		Field: 1,
235 | 	}
236 | 
237 | 	if err := mergo.MergeValue(&dst, src); err != nil {
238 | 		t.Error(err)
239 | 	}
240 | 
241 | 	if dst.Field != src.Field {
242 | 		t.Errorf("Expected dst.Field to be %d, got %d", src.Field, dst.Field)
243 | 	}
244 | }
245 | 
246 | func TestMergeValueNil(t *testing.T) {
247 | 	t.Parallel()
248 | 
249 | 	var naerr *mergo.NilArgumentsError
250 | 
251 | 	if err := mergo.MergeValue(nil, struct{}{}); !errors.As(err, &naerr) {
252 | 		t.Error(err)
253 | 	}
254 | }
255 | 
256 | func TestMergePtr(t *testing.T) {
257 | 	t.Parallel()
258 | 
259 | 	type ts struct {
260 | 		Field int
261 | 	}
262 | 
263 | 	var (
264 | 		dst = &ts{
265 | 			Field: 0,
266 | 		}
267 | 		src = &ts{
268 | 			Field: 1,
269 | 		}
270 | 	)
271 | 
272 | 	if err := mergo.MergePtr(dst, src); err != nil {
273 | 		t.Error(err)
274 | 	}
275 | 
276 | 	if dst.Field != src.Field {
277 | 		t.Errorf("Expected dst.Field to be %d, got %d", src.Field, dst.Field)
278 | 	}
279 | }
280 | 
281 | func TestMergePtrNil(t *testing.T) {
282 | 	t.Parallel()
283 | 
284 | 	type ts struct {
285 | 		Field int
286 | 	}
287 | 
288 | 	t.Run("dst nil", func(t *testing.T) {
289 | 		t.Parallel()
290 | 
291 | 		var (
292 | 			src = &ts{
293 | 				Field: 0,
294 | 			}
295 | 			naerr *mergo.NilArgumentsError
296 | 		)
297 | 
298 | 		if err := mergo.MergePtr(nil, src); !errors.As(err, &naerr) {
299 | 			t.Error(err)
300 | 		}
301 | 	})
302 | 
303 | 	t.Run("src nil", func(t *testing.T) {
304 | 		t.Parallel()
305 | 
306 | 		var (
307 | 			dst = &ts{
308 | 				Field: 0,
309 | 			}
310 | 			naerr *mergo.NilArgumentsError
311 | 		)
312 | 
313 | 		if err := mergo.MergePtr(dst, nil); !errors.As(err, &naerr) {
314 | 			t.Error(err)
315 | 		}
316 | 	})
317 | 
318 | 	t.Run("both nil", func(t *testing.T) {
319 | 		t.Parallel()
320 | 
321 | 		var (
322 | 			dst   *ts
323 | 			src   *ts
324 | 			naerr *mergo.NilArgumentsError
325 | 		)
326 | 
327 | 		if err := mergo.MergePtr(dst, src); !errors.As(err, &naerr) {
328 | 			t.Error(err)
329 | 		}
330 | 	})
331 | }
332 | 
333 | func TestErrorMessages(t *testing.T) {
334 | 	t.Parallel()
335 | 
336 | 	t.Run("NilArgumentsError", func(t *testing.T) {
337 | 		t.Parallel()
338 | 
339 | 		msg := "src and dst must not be nil"
340 | 		if err := mergo.Merge(nil, nil); err.Error() != msg {
341 | 			t.Errorf("Expected error to be %q, got %q", msg, err.Error())
342 | 		}
343 | 	})
344 | 
345 | 	t.Run("InvalidDestinationError", func(t *testing.T) {
346 | 		t.Parallel()
347 | 
348 | 		msg := "dst must be a pointer"
349 | 		if err := mergo.Merge(struct{}{}, struct{}{}); err.Error() != msg {
350 | 			t.Errorf("Expected error to be %q, got %q", msg, err.Error())
351 | 		}
352 | 	})
353 | 
354 | 	t.Run("DifferentArgumentTypesError", func(t *testing.T) {
355 | 		t.Parallel()
356 | 
357 | 		dst := struct {
358 | 			Field int
359 | 		}{
360 | 			Field: 1,
361 | 		}
362 | 
363 | 		src := struct {
364 | 			Field string
365 | 		}{
366 | 			Field: "src",
367 | 		}
368 | 
369 | 		msg := "dst and src must have the same type"
370 | 		if err := mergo.Merge(&dst, src); err.Error() != msg {
371 | 			t.Errorf("Expected error to be %q, got %q", msg, err.Error())
372 | 		}
373 | 	})
374 | }
375 | 
376 | func BenchmarkMerge(b *testing.B) {
377 | 	b.ReportAllocs()
378 | 
379 | 	type ts struct {
380 | 		Field int
381 | 	}
382 | 
383 | 	src := ts{
384 | 		Field: 2,
385 | 	}
386 | 
387 | 	b.Run("Merge", func(b *testing.B) {
388 | 		dst := ts{
389 | 			Field: 0,
390 | 		}
391 | 
392 | 		b.ResetTimer()
393 | 
394 | 		for i := 0; i < b.N; i++ {
395 | 			if err := mergo.Merge(&dst, src); err != nil {
396 | 				b.Fatal(err)
397 | 			}
398 | 
399 | 			dst.Field = 0
400 | 		}
401 | 	})
402 | 
403 | 	b.Run("MergeValue", func(b *testing.B) {
404 | 		dst := ts{
405 | 			Field: 0,
406 | 		}
407 | 
408 | 		b.ResetTimer()
409 | 
410 | 		for i := 0; i < b.N; i++ {
411 | 			if err := mergo.MergeValue(&dst, src); err != nil {
412 | 				b.Fatal(err)
413 | 			}
414 | 
415 | 			dst.Field = 0
416 | 		}
417 | 	})
418 | 
419 | 	b.Run("MergePtr", func(b *testing.B) {
420 | 		dst := &ts{
421 | 			Field: 0,
422 | 		}
423 | 
424 | 		b.ResetTimer()
425 | 
426 | 		for i := 0; i < b.N; i++ {
427 | 			if err := mergo.MergePtr(dst, &src); err != nil {
428 | 				b.Fatal(err)
429 | 			}
430 | 
431 | 			dst.Field = 0
432 | 		}
433 | 	})
434 | }
435 | 


--------------------------------------------------------------------------------
/issue100_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type issue100s struct {
10 | 	Member interface{}
11 | }
12 | 
13 | func TestIssue100(t *testing.T) {
14 | 	m := make(map[string]interface{})
15 | 	m["Member"] = "anything"
16 | 
17 | 	st := &issue100s{}
18 | 	if err := mergo.Map(st, m); err != nil {
19 | 		t.Error(err)
20 | 	}
21 | }
22 | 


--------------------------------------------------------------------------------
/issue104_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type Record struct {
11 | 	Data    map[string]interface{}
12 | 	Mapping map[string]string
13 | }
14 | 
15 | func StructToRecord(in interface{}) *Record {
16 | 	rec := Record{}
17 | 	rec.Data = make(map[string]interface{})
18 | 	rec.Mapping = make(map[string]string)
19 | 	typ := reflect.TypeOf(in)
20 | 	for i := 0; i < typ.NumField(); i++ {
21 | 		field := typ.Field(i)
22 | 		dbFieldName := field.Tag.Get("db")
23 | 		if dbFieldName != "" {
24 | 			rec.Mapping[field.Name] = dbFieldName
25 | 		}
26 | 	}
27 | 
28 | 	if err := mergo.Map(&rec.Data, in); err != nil {
29 | 		panic(err)
30 | 	}
31 | 	return &rec
32 | }
33 | 
34 | func TestStructToRecord(t *testing.T) {
35 | 	type A struct {
36 | 		Name string `json:"name" db:"name"`
37 | 		CIDR string `json:"cidr" db:"cidr"`
38 | 	}
39 | 	a := A{Name: "David", CIDR: "10.0.0.0/8"}
40 | 	rec := StructToRecord(a)
41 | 	if len(rec.Mapping) < 2 {
42 | 		t.Fatalf("struct to record failed, no mapping, struct missing tags?, rec: %+v, a: %+v ", rec, a)
43 | 	}
44 | }
45 | 


--------------------------------------------------------------------------------
/issue121_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func TestIssue121WithSliceDeepCopy(t *testing.T) {
10 | 	dst := map[string]interface{}{
11 | 		"inter": map[string]interface{}{
12 | 			"a": "1",
13 | 			"b": "2",
14 | 		},
15 | 	}
16 | 
17 | 	src := map[string]interface{}{
18 | 		"inter": map[string]interface{}{
19 | 			"a": "3",
20 | 			"c": "4",
21 | 		},
22 | 	}
23 | 
24 | 	if err := mergo.Merge(&dst, src, mergo.WithSliceDeepCopy); err != nil {
25 | 		t.Errorf("Error during the merge: %v", err)
26 | 	}
27 | 
28 | 	if dst["inter"].(map[string]interface{})["a"].(string) != "3" {
29 | 		t.Error("inter.a should equal '3'")
30 | 	}
31 | 
32 | 	if dst["inter"].(map[string]interface{})["c"].(string) != "4" {
33 | 		t.Error("inter.c should equal '4'")
34 | 	}
35 | }
36 | 


--------------------------------------------------------------------------------
/issue123_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func TestIssue123(t *testing.T) {
10 | 	src := map[string]interface{}{
11 | 		"col1": nil,
12 | 		"col2": 4,
13 | 		"col3": nil,
14 | 	}
15 | 	dst := map[string]interface{}{
16 | 		"col1": 2,
17 | 		"col2": 3,
18 | 		"col3": 3,
19 | 	}
20 | 
21 | 	// Expected behavior
22 | 	if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
23 | 		t.Fatal(err)
24 | 	}
25 | 	testCases := []struct {
26 | 		expected interface{}
27 | 		key      string
28 | 	}{
29 | 		{
30 | 			nil,
31 | 			"col1",
32 | 		},
33 | 		{
34 | 			4,
35 | 			"col2",
36 | 		},
37 | 		{
38 | 			nil,
39 | 			"col3",
40 | 		},
41 | 	}
42 | 	for _, tC := range testCases {
43 | 		if dst[tC.key] != tC.expected {
44 | 			t.Fatalf("expected %v in dst[%q], got %v", tC.expected, tC.key, dst[tC.key])
45 | 		}
46 | 	}
47 | }
48 | 


--------------------------------------------------------------------------------
/issue125_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type settings struct {
11 | 	FirstSlice  []string `json:"FirstSlice"`
12 | 	SecondSlice []string `json:"SecondSlice"`
13 | }
14 | 
15 | func TestIssue125MergeWithOverwrite(t *testing.T) {
16 | 	var (
17 | 		defaultSettings = settings{
18 | 			FirstSlice:  []string{},
19 | 			SecondSlice: []string{},
20 | 		}
21 | 		something settings
22 | 		data      = `{"FirstSlice":[], "SecondSlice": null}`
23 | 	)
24 | 
25 | 	if err := json.Unmarshal([]byte(data), &something); err != nil {
26 | 		t.Errorf("Error while Unmarshalling maprequest: %s", err)
27 | 	}
28 | 
29 | 	if err := mergo.Merge(&something, defaultSettings, mergo.WithOverrideEmptySlice); err != nil {
30 | 		t.Errorf("Error while merging: %s", err)
31 | 	}
32 | 
33 | 	if something.FirstSlice == nil {
34 | 		t.Error("Invalid merging first slice")
35 | 	}
36 | 
37 | 	if something.SecondSlice == nil {
38 | 		t.Error("Invalid merging second slice")
39 | 	}
40 | }
41 | 


--------------------------------------------------------------------------------
/issue129_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func TestIssue129Boolean(t *testing.T) {
10 | 	type Foo struct {
11 | 		A bool
12 | 		B bool
13 | 	}
14 | 
15 | 	src := Foo{
16 | 		A: true,
17 | 		B: false,
18 | 	}
19 | 	dst := Foo{
20 | 		A: false,
21 | 		B: true,
22 | 	}
23 | 
24 | 	// Standard behavior
25 | 	if err := mergo.Merge(&dst, src); err != nil {
26 | 		t.Error(err)
27 | 	}
28 | 	if dst.A != true {
29 | 		t.Errorf("expected true, got false")
30 | 	}
31 | 	if dst.B != true {
32 | 		t.Errorf("expected true, got false")
33 | 	}
34 | 
35 | 	// Expected behavior
36 | 	dst = Foo{
37 | 		A: false,
38 | 		B: true,
39 | 	}
40 | 	if err := mergo.Merge(&dst, src, mergo.WithOverwriteWithEmptyValue); err != nil {
41 | 		t.Error(err)
42 | 	}
43 | 	if dst.A != true {
44 | 		t.Errorf("expected true, got false")
45 | 	}
46 | 	if dst.B != false {
47 | 		t.Errorf("expected false, got true")
48 | 	}
49 | }
50 | 


--------------------------------------------------------------------------------
/issue131_test.go:
--------------------------------------------------------------------------------
  1 | package mergo_test
  2 | 
  3 | import (
  4 | 	"testing"
  5 | 
  6 | 	"dario.cat/mergo"
  7 | )
  8 | 
  9 | type foz struct {
 10 | 	A *bool
 11 | 	B string
 12 | 	C *bool
 13 | 	D *bool
 14 | 	E *bool
 15 | 	F *baz
 16 | }
 17 | 
 18 | type baz struct {
 19 | 	A *bool
 20 | }
 21 | 
 22 | func TestIssue131MergeWithOverwriteWithEmptyValue(t *testing.T) {
 23 | 	src := foz{
 24 | 		A: func(v bool) *bool { return &v }(false),
 25 | 		B: "src",
 26 | 	}
 27 | 	dest := foz{
 28 | 		A: func(v bool) *bool { return &v }(true),
 29 | 		B: "dest",
 30 | 	}
 31 | 	if err := mergo.Merge(&dest, src, mergo.WithOverwriteWithEmptyValue); err != nil {
 32 | 		t.Error(err)
 33 | 	}
 34 | 	if *src.A != *dest.A {
 35 | 		t.Errorf("dest.A not merged in properly: %v != %v", *src.A, *dest.A)
 36 | 	}
 37 | 	if src.B != dest.B {
 38 | 		t.Errorf("dest.B not merged in properly: %v != %v", src.B, dest.B)
 39 | 	}
 40 | }
 41 | 
 42 | func TestIssue131MergeWithoutDereferenceWithOverride(t *testing.T) {
 43 | 	src := foz{
 44 | 		A: func(v bool) *bool { return &v }(false),
 45 | 		B: "src",
 46 | 		C: nil,
 47 | 		D: func(v bool) *bool { return &v }(false),
 48 | 		E: func(v bool) *bool { return &v }(true),
 49 | 	}
 50 | 	dest := foz{
 51 | 		A: func(v bool) *bool { return &v }(true),
 52 | 		B: "dest",
 53 | 		C: func(v bool) *bool { return &v }(false),
 54 | 		D: nil,
 55 | 		E: func(v bool) *bool { return &v }(false),
 56 | 	}
 57 | 	if err := mergo.Merge(&dest, src, mergo.WithoutDereference, mergo.WithOverride); err != nil {
 58 | 		t.Error(err)
 59 | 	}
 60 | 	if *src.A != *dest.A {
 61 | 		t.Errorf("dest.A not merged in properly: %v != %v", *src.A, *dest.A)
 62 | 	}
 63 | 	if src.B != dest.B {
 64 | 		t.Errorf("dest.B not merged in properly: %v != %v", src.B, dest.B)
 65 | 	}
 66 | 	if *dest.C != false {
 67 | 		t.Errorf("dest.C not merged in properly: %v != %v", *src.C, *dest.C)
 68 | 	}
 69 | 	if *dest.D != false {
 70 | 		t.Errorf("dest.D not merged in properly: %v != %v", src.D, *dest.D)
 71 | 	}
 72 | 	if *dest.E != true {
 73 | 		t.Errorf("dest.E not merged in properly: %v != %v", *src.E, *dest.E)
 74 | 	}
 75 | }
 76 | 
 77 | func TestIssue131MergeWithoutDereference(t *testing.T) {
 78 | 	src := foz{
 79 | 		A: func(v bool) *bool { return &v }(false),
 80 | 		B: "src",
 81 | 		C: nil,
 82 | 		D: func(v bool) *bool { return &v }(false),
 83 | 		E: func(v bool) *bool { return &v }(true),
 84 | 		F: &baz{
 85 | 			A: func(v bool) *bool { return &v }(true),
 86 | 		},
 87 | 	}
 88 | 	dest := foz{
 89 | 		A: func(v bool) *bool { return &v }(true),
 90 | 		B: "dest",
 91 | 		C: func(v bool) *bool { return &v }(false),
 92 | 		D: nil,
 93 | 		E: func(v bool) *bool { return &v }(false),
 94 | 		F: nil,
 95 | 	}
 96 | 	if err := mergo.Merge(&dest, src, mergo.WithoutDereference); err != nil {
 97 | 		t.Error(err)
 98 | 	}
 99 | 	if *src.A == *dest.A {
100 | 		t.Errorf("dest.A should not have been merged: %v == %v", *src.A, *dest.A)
101 | 	}
102 | 	if src.B == dest.B {
103 | 		t.Errorf("dest.B should not have been merged: %v == %v", src.B, dest.B)
104 | 	}
105 | 	if *dest.C != false {
106 | 		t.Errorf("dest.C not merged in properly: %v != %v", *src.C, *dest.C)
107 | 	}
108 | 	if *dest.D != false {
109 | 		t.Errorf("dest.D not merged in properly: %v != %v", src.D, *dest.D)
110 | 	}
111 | 	if *dest.E == true {
112 | 		t.Errorf("dest.E should not have been merged: %v == %v", *src.E, *dest.E)
113 | 	}
114 | 
115 | 	if dest.F == nil {
116 | 		t.Errorf("dest.F should not have been overridden with nil: %v == %v", src.F, dest.F)
117 | 	}
118 | 
119 | 	if *dest.F.A == false {
120 | 		t.Errorf("dest.F.A not merged in properly: %v != %v", *src.F.A, *dest.F.A)
121 | 	}
122 | }
123 | 


--------------------------------------------------------------------------------
/issue136_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type embeddedTestA struct {
10 | 	Name string
11 | 	Age  uint8
12 | }
13 | 
14 | type embeddedTestB struct {
15 | 	Address string
16 | 	embeddedTestA
17 | }
18 | 
19 | func TestMergeEmbedded(t *testing.T) {
20 | 	var (
21 | 		err error
22 | 		a   = &embeddedTestA{
23 | 			"Suwon", 16,
24 | 		}
25 | 		b = &embeddedTestB{}
26 | 	)
27 | 
28 | 	if err := mergo.Merge(&b.embeddedTestA, *a); err != nil {
29 | 		t.Error(err)
30 | 	}
31 | 
32 | 	if b.Name != "Suwon" {
33 | 		t.Errorf("%v %v", b.Name, err)
34 | 	}
35 | }
36 | 


--------------------------------------------------------------------------------
/issue138_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | const issue138configuration string = `
11 | {
12 | 	"Port": 80
13 | }
14 | `
15 | 
16 | func TestIssue138(t *testing.T) {
17 | 	type config struct {
18 | 		Port uint16
19 | 	}
20 | 	type compatibleConfig struct {
21 | 		Port float64
22 | 	}
23 | 
24 | 	foo := make(map[string]interface{})
25 | 	// encoding/json unmarshals numbers as float64
26 | 	// https://golang.org/pkg/encoding/json/#Unmarshal
27 | 	_ = json.Unmarshal([]byte(issue138configuration), &foo)
28 | 
29 | 	err := mergo.Map(&config{}, foo)
30 | 	if err == nil {
31 | 		t.Error("expected type mismatch error, got nil")
32 | 	} else {
33 | 		if err.Error() != "type mismatch on Port field: found float64, expected uint16" {
34 | 			t.Errorf("expected type mismatch error, got %q", err)
35 | 		}
36 | 	}
37 | 
38 | 	c := compatibleConfig{}
39 | 	if err := mergo.Map(&c, foo); err != nil {
40 | 		t.Error(err)
41 | 	}
42 | }
43 | 


--------------------------------------------------------------------------------
/issue143_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"fmt"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | func TestIssue143(t *testing.T) {
11 | 	testCases := []struct {
12 | 		expected func(map[string]interface{}) error
13 | 		options  []func(*mergo.Config)
14 | 	}{
15 | 		{
16 | 			options: []func(*mergo.Config){mergo.WithOverride},
17 | 			expected: func(m map[string]interface{}) error {
18 | 				properties := m["properties"].(map[string]interface{})
19 | 				if properties["field1"] != "wrong" {
20 | 					return fmt.Errorf("expected %q, got %v", "wrong", properties["field1"])
21 | 				}
22 | 				return nil
23 | 			},
24 | 		},
25 | 		{
26 | 			options: []func(*mergo.Config){},
27 | 			expected: func(m map[string]interface{}) error {
28 | 				properties := m["properties"].(map[string]interface{})
29 | 				if properties["field1"] == "wrong" {
30 | 					return fmt.Errorf("expected a map, got %v", "wrong")
31 | 				}
32 | 				return nil
33 | 			},
34 | 		},
35 | 	}
36 | 	for _, tC := range testCases {
37 | 		base := map[string]interface{}{
38 | 			"properties": map[string]interface{}{
39 | 				"field1": map[string]interface{}{
40 | 					"type": "text",
41 | 				},
42 | 			},
43 | 		}
44 | 
45 | 		err := mergo.Map(
46 | 			&base,
47 | 			map[string]interface{}{
48 | 				"properties": map[string]interface{}{
49 | 					"field1": "wrong",
50 | 				},
51 | 			},
52 | 			tC.options...,
53 | 		)
54 | 		if err != nil {
55 | 			t.Error(err)
56 | 		}
57 | 		if err := tC.expected(base); err != nil {
58 | 			t.Error(err)
59 | 		}
60 | 	}
61 | }
62 | 


--------------------------------------------------------------------------------
/issue149_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type user struct {
10 | 	Name string
11 | }
12 | 
13 | type token struct {
14 | 	User  *user
15 | 	Token *string
16 | }
17 | 
18 | func TestIssue149(t *testing.T) {
19 | 	dest := &token{
20 | 		User: &user{
21 | 			Name: "destination",
22 | 		},
23 | 		Token: nil,
24 | 	}
25 | 	tokenValue := "Issue149"
26 | 	src := &token{
27 | 		User:  nil,
28 | 		Token: &tokenValue,
29 | 	}
30 | 	if err := mergo.Merge(dest, src, mergo.WithOverwriteWithEmptyValue); err != nil {
31 | 		t.Error(err)
32 | 	}
33 | 	if dest.User != nil {
34 | 		t.Errorf("expected nil User, got %q", dest.User)
35 | 	}
36 | 	if dest.Token == nil {
37 | 		t.Errorf("expected not nil Token, got %q", *dest.Token)
38 | 	}
39 | }
40 | 


--------------------------------------------------------------------------------
/issue174_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type structWithBlankField struct {
10 | 	_ struct{}
11 | 	A struct{}
12 | }
13 | 
14 | func TestIssue174(t *testing.T) {
15 | 	dst := structWithBlankField{}
16 | 	src := structWithBlankField{}
17 | 
18 | 	if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
19 | 		t.Error(err)
20 | 	}
21 | }
22 | 


--------------------------------------------------------------------------------
/issue17_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"encoding/json"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | func TestIssue17MergeWithOverwrite(t *testing.T) {
11 | 	var (
12 | 		request    = `{"timestamp":null, "name": "foo"}`
13 | 		maprequest = map[string]interface{}{
14 | 			"timestamp": nil,
15 | 			"name":      "foo",
16 | 			"newStuff":  "foo",
17 | 		}
18 | 	)
19 | 
20 | 	var something map[string]interface{}
21 | 	if err := json.Unmarshal([]byte(request), &something); err != nil {
22 | 		t.Errorf("Error while Unmarshalling maprequest: %s", err)
23 | 	}
24 | 
25 | 	if err := mergo.MergeWithOverwrite(&something, maprequest); err != nil {
26 | 		t.Errorf("Error while merging: %s", err)
27 | 	}
28 | }
29 | 


--------------------------------------------------------------------------------
/issue187_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"dario.cat/mergo"
 5 | 	"testing"
 6 | )
 7 | 
 8 | func TestIssue187MergeStructToMap(t *testing.T) {
 9 | 	dst := map[string]interface{}{
10 | 		"empty": "data",
11 | 	}
12 | 
13 | 	src := struct {
14 | 		Foo   string
15 | 		Bar   int
16 | 		Empty string
17 | 	}{
18 | 		Foo: "hello",
19 | 		Bar: 42,
20 | 	}
21 | 	if err := mergo.Map(&dst, src); err != nil {
22 | 		t.Error(err)
23 | 	}
24 | 	if dst["foo"] != "hello" || dst["bar"] != 42 || dst["empty"] != "data" {
25 | 		t.Errorf("expected dst to be {foo: hello, bar: 42, empty: data}, got {foo: %v, bar: %v, empty: %v}", dst["foo"], dst["bar"], dst["empty"])
26 | 	}
27 | }
28 | 
29 | func TestIssue187MergeStructToMapWithOverwrite(t *testing.T) {
30 | 	dst := map[string]interface{}{
31 | 		"foo":   "initial",
32 | 		"bar":   1,
33 | 		"empty": "data",
34 | 	}
35 | 	src := struct {
36 | 		Foo   string
37 | 		Bar   int
38 | 		Empty string
39 | 	}{
40 | 		Foo: "hello",
41 | 		Bar: 42,
42 | 	}
43 | 	if err := mergo.Map(&dst, src, mergo.WithOverride); err != nil {
44 | 		t.Error(err)
45 | 	}
46 | 	if dst["foo"] != "hello" || dst["bar"] != 42 || dst["empty"] != "data" {
47 | 		t.Errorf("expected dst to be {foo: hello, bar: 42, empty: data}, got {foo: %v, bar: %v, empty: %v}", dst["foo"], dst["bar"], dst["empty"])
48 | 	}
49 | }
50 | 
51 | func TestIssue187MergeStructToMapWithOverwriteWithEmptyValue(t *testing.T) {
52 | 	dst := map[string]interface{}{
53 | 		"foo":   "initial",
54 | 		"bar":   1,
55 | 		"empty": "data",
56 | 	}
57 | 	src := struct {
58 | 		Foo   string
59 | 		Bar   int
60 | 		Empty string
61 | 	}{
62 | 		Foo: "hello",
63 | 		Bar: 42,
64 | 	}
65 | 	if err := mergo.Map(&dst, src, mergo.WithOverwriteWithEmptyValue); err != nil {
66 | 		t.Error(err)
67 | 	}
68 | 	if dst["foo"] != "hello" || dst["bar"] != 42 || dst["empty"] != "" {
69 | 		t.Errorf("expected dst to be {foo: hello, bar: 42, empty: }, got {foo: %v, bar: %v, empty: %v}", dst["foo"], dst["bar"], dst["empty"])
70 | 	}
71 | }
72 | 


--------------------------------------------------------------------------------
/issue202_test.go:
--------------------------------------------------------------------------------
  1 | package mergo_test
  2 | 
  3 | import (
  4 | 	"reflect"
  5 | 	"testing"
  6 | 
  7 | 	"dario.cat/mergo"
  8 | )
  9 | 
 10 | func TestIssue202(t *testing.T) {
 11 | 	tests := []struct {
 12 | 		name           string
 13 | 		dst, src, want map[string]interface{}
 14 | 	}{
 15 | 		{
 16 | 			name: "slice override string",
 17 | 			dst: map[string]interface{}{
 18 | 				"x": 456,
 19 | 				"y": "foo",
 20 | 			},
 21 | 			src: map[string]interface{}{
 22 | 				"x": "123",
 23 | 				"y": []int{1, 2, 3},
 24 | 			},
 25 | 			want: map[string]interface{}{
 26 | 				"x": "123",
 27 | 				"y": []int{1, 2, 3},
 28 | 			},
 29 | 		},
 30 | 		{
 31 | 			name: "string override slice",
 32 | 			dst: map[string]interface{}{
 33 | 				"x": 456,
 34 | 				"y": []int{1, 2, 3},
 35 | 			},
 36 | 			src: map[string]interface{}{
 37 | 				"x": "123",
 38 | 				"y": "foo",
 39 | 			},
 40 | 			want: map[string]interface{}{
 41 | 				"x": "123",
 42 | 				"y": "foo",
 43 | 			},
 44 | 		},
 45 | 		{
 46 | 			name: "map override string",
 47 | 			dst: map[string]interface{}{
 48 | 				"x": 456,
 49 | 				"y": "foo",
 50 | 			},
 51 | 			src: map[string]interface{}{
 52 | 				"x": "123",
 53 | 				"y": map[string]interface{}{
 54 | 					"a": true,
 55 | 				},
 56 | 			},
 57 | 			want: map[string]interface{}{
 58 | 				"x": "123",
 59 | 				"y": map[string]interface{}{
 60 | 					"a": true,
 61 | 				},
 62 | 			},
 63 | 		},
 64 | 		{
 65 | 			name: "string override map",
 66 | 			dst: map[string]interface{}{
 67 | 				"x": 456,
 68 | 				"y": map[string]interface{}{
 69 | 					"a": true,
 70 | 				},
 71 | 			},
 72 | 			src: map[string]interface{}{
 73 | 				"x": "123",
 74 | 				"y": "foo",
 75 | 			},
 76 | 			want: map[string]interface{}{
 77 | 				"x": "123",
 78 | 				"y": "foo",
 79 | 			},
 80 | 		},
 81 | 		{
 82 | 			name: "map override map",
 83 | 			dst: map[string]interface{}{
 84 | 				"x": 456,
 85 | 				"y": map[string]interface{}{
 86 | 					"a": 10,
 87 | 				},
 88 | 			},
 89 | 			src: map[string]interface{}{
 90 | 				"x": "123",
 91 | 				"y": map[string]interface{}{
 92 | 					"a": true,
 93 | 				},
 94 | 			},
 95 | 			want: map[string]interface{}{
 96 | 				"x": "123",
 97 | 				"y": map[string]interface{}{
 98 | 					"a": true,
 99 | 				},
100 | 			},
101 | 		},
102 | 		{
103 | 			name: "map override map with merge",
104 | 			dst: map[string]interface{}{
105 | 				"x": 456,
106 | 				"y": map[string]interface{}{
107 | 					"a": 10,
108 | 					"b": 100,
109 | 				},
110 | 			},
111 | 			src: map[string]interface{}{
112 | 				"x": "123",
113 | 				"y": map[string]interface{}{
114 | 					"a": true,
115 | 				},
116 | 			},
117 | 			want: map[string]interface{}{
118 | 				"x": "123",
119 | 				"y": map[string]interface{}{
120 | 					"a": true,
121 | 					"b": 100,
122 | 				},
123 | 			},
124 | 		},
125 | 	}
126 | 
127 | 	for _, tt := range tests {
128 | 		t.Run(tt.name, func(t *testing.T) {
129 | 			if err := mergo.Merge(&tt.dst, tt.src, mergo.WithOverride); err != nil {
130 | 				t.Error(err)
131 | 			}
132 | 
133 | 			if !reflect.DeepEqual(tt.dst, tt.want) {
134 | 				t.Errorf("maps not equal.\nwant:\n%v\ngot:\n%v\n", tt.want, tt.dst)
135 | 			}
136 | 		})
137 | 	}
138 | }
139 | 


--------------------------------------------------------------------------------
/issue209_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func TestIssue209(t *testing.T) {
10 | 	dst := []string{"a", "b"}
11 | 	src := []string{"c", "d"}
12 | 
13 | 	if err := mergo.Merge(&dst, src, mergo.WithAppendSlice); err != nil {
14 | 		t.Error(err)
15 | 	}
16 | 
17 | 	expected := []string{"a", "b", "c", "d"}
18 | 	if len(dst) != len(expected) {
19 | 		t.Errorf("arrays not equal length")
20 | 	}
21 | 	for i := range expected {
22 | 		if dst[i] != expected[i] {
23 | 			t.Errorf("array elements at %d are not equal", i)
24 | 		}
25 | 	}
26 | }
27 | 


--------------------------------------------------------------------------------
/issue220_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | func TestIssue220(t *testing.T) {
11 | 	dst := []interface{}{
12 | 		map[string]int{
13 | 			"a": 1,
14 | 		},
15 | 	}
16 | 	src := []interface{}{
17 | 		"nil",
18 | 	}
19 | 	expected := []interface{}{
20 | 		map[string]int{
21 | 			"a": 1,
22 | 		},
23 | 	}
24 | 
25 | 	err := mergo.Merge(&dst, src, mergo.WithSliceDeepCopy)
26 | 	if err != nil {
27 | 		t.Errorf("unexpected error %v", err)
28 | 	}
29 | 
30 | 	if !reflect.DeepEqual(dst, expected) {
31 | 		t.Errorf("expected: %#v\ngot: %#v", expected, dst)
32 | 	}
33 | }
34 | 


--------------------------------------------------------------------------------
/issue230_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | var testDataM = []struct {
10 | 	M1                     mapTest
11 | 	M2                     mapTest
12 | 	WithOverrideEmptyValue bool
13 | 	ExpectedMap            map[int]int
14 | }{
15 | 	{
16 | 		M1: mapTest{
17 | 			M: map[int]int{1: 1, 3: 3},
18 | 		},
19 | 		M2: mapTest{
20 | 			M: map[int]int{1: 2, 2: 2},
21 | 		},
22 | 		WithOverrideEmptyValue: true,
23 | 		ExpectedMap:            map[int]int{1: 1, 3: 3},
24 | 	},
25 | 	{
26 | 		M1: mapTest{
27 | 			M: map[int]int{1: 1, 3: 3},
28 | 		},
29 | 		M2: mapTest{
30 | 			M: map[int]int{1: 2, 2: 2},
31 | 		},
32 | 		WithOverrideEmptyValue: false,
33 | 		ExpectedMap:            map[int]int{1: 1, 2: 2, 3: 3},
34 | 	},
35 | 	{
36 | 		M1: mapTest{
37 | 			M: map[int]int{},
38 | 		},
39 | 		M2: mapTest{
40 | 			M: map[int]int{1: 2, 2: 2},
41 | 		},
42 | 		WithOverrideEmptyValue: true,
43 | 		ExpectedMap:            map[int]int{},
44 | 	},
45 | 	{
46 | 		M1: mapTest{
47 | 			M: map[int]int{},
48 | 		},
49 | 		M2: mapTest{
50 | 			M: map[int]int{1: 2, 2: 2},
51 | 		},
52 | 		WithOverrideEmptyValue: false,
53 | 		ExpectedMap:            map[int]int{1: 2, 2: 2},
54 | 	},
55 | }
56 | 
57 | func withOverrideEmptyValue(enable bool) func(*mergo.Config) {
58 | 	if enable {
59 | 		return mergo.WithOverwriteWithEmptyValue
60 | 	}
61 | 
62 | 	return mergo.WithOverride
63 | }
64 | 
65 | func TestMergeMapWithOverride(t *testing.T) {
66 | 	t.Parallel()
67 | 
68 | 	for _, data := range testDataM {
69 | 		err := mergo.Merge(&data.M2, data.M1, withOverrideEmptyValue(data.WithOverrideEmptyValue))
70 | 		if err != nil {
71 | 			t.Errorf("Error while merging %s", err)
72 | 		}
73 | 
74 | 		if len(data.M2.M) != len(data.ExpectedMap) {
75 | 			t.Errorf("Got %d elements in map, but expected %d", len(data.M2.M), len(data.ExpectedMap))
76 | 			return
77 | 		}
78 | 
79 | 		for i, val := range data.M2.M {
80 | 			if val != data.ExpectedMap[i] {
81 | 				t.Errorf("Expected value: %d, but got %d while merging map", data.ExpectedMap[i], val)
82 | 			}
83 | 		}
84 | 	}
85 | }
86 | 


--------------------------------------------------------------------------------
/issue23_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 	"time"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type document struct {
11 | 	Created *time.Time
12 | }
13 | 
14 | func TestIssue23MergeWithOverwrite(t *testing.T) {
15 | 	now := time.Now()
16 | 	dst := document{
17 | 		&now,
18 | 	}
19 | 	expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
20 | 	src := document{
21 | 		&expected,
22 | 	}
23 | 
24 | 	if err := mergo.MergeWithOverwrite(&dst, src); err != nil {
25 | 		t.Errorf("Error while merging %s", err)
26 | 	}
27 | 
28 | 	if !dst.Created.Equal(*src.Created) { //--> https://golang.org/pkg/time/#pkg-overview
29 | 		t.Errorf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
30 | 	}
31 | }
32 | 


--------------------------------------------------------------------------------
/issue33_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type Foo struct {
10 | 	Str    string
11 | 	Bslice []byte
12 | }
13 | 
14 | func TestIssue33Merge(t *testing.T) {
15 | 	dest := Foo{Str: "a"}
16 | 	toMerge := Foo{
17 | 		Str:    "b",
18 | 		Bslice: []byte{1, 2},
19 | 	}
20 | 
21 | 	if err := mergo.Merge(&dest, toMerge); err != nil {
22 | 		t.Errorf("Error while merging: %s", err)
23 | 	}
24 | 	// Merge doesn't overwrite an attribute if in destination it doesn't have a zero value.
25 | 	// In this case, Str isn't a zero value string.
26 | 	if dest.Str != "a" {
27 | 		t.Errorf("dest.Str should have not been override as it has a non-zero value: dest.Str(%v) != 'a'", dest.Str)
28 | 	}
29 | 	// If we want to override, we must use MergeWithOverwrite or Merge using WithOverride.
30 | 	if err := mergo.Merge(&dest, toMerge, mergo.WithOverride); err != nil {
31 | 		t.Errorf("Error while merging: %s", err)
32 | 	}
33 | 
34 | 	if dest.Str != toMerge.Str {
35 | 		t.Errorf("dest.Str should have been override: dest.Str(%v) != toMerge.Str(%v)", dest.Str, toMerge.Str)
36 | 	}
37 | }
38 | 


--------------------------------------------------------------------------------
/issue38_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 	"time"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type structWithoutTimePointer struct {
11 | 	Created time.Time
12 | }
13 | 
14 | func TestIssue38Merge(t *testing.T) {
15 | 	dst := structWithoutTimePointer{
16 | 		time.Now(),
17 | 	}
18 | 
19 | 	expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
20 | 	src := structWithoutTimePointer{
21 | 		expected,
22 | 	}
23 | 
24 | 	if err := mergo.Merge(&dst, src); err != nil {
25 | 		t.Errorf("Error while merging %s", err)
26 | 	}
27 | 
28 | 	if dst.Created.Equal(src.Created) {
29 | 		t.Errorf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
30 | 	}
31 | }
32 | 
33 | func TestIssue38MergeEmptyStruct(t *testing.T) {
34 | 	dst := structWithoutTimePointer{}
35 | 
36 | 	expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
37 | 	src := structWithoutTimePointer{
38 | 		expected,
39 | 	}
40 | 
41 | 	if err := mergo.Merge(&dst, src); err != nil {
42 | 		t.Errorf("Error while merging %s", err)
43 | 	}
44 | 
45 | 	if dst.Created.Equal(src.Created) {
46 | 		t.Errorf("Created merged unexpectedly: dst.Created(%v) == src.Created(%v)", dst.Created, src.Created)
47 | 	}
48 | }
49 | 
50 | func TestIssue38MergeWithOverwrite(t *testing.T) {
51 | 	dst := structWithoutTimePointer{
52 | 		time.Now(),
53 | 	}
54 | 
55 | 	expected := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
56 | 	src := structWithoutTimePointer{
57 | 		expected,
58 | 	}
59 | 
60 | 	if err := mergo.MergeWithOverwrite(&dst, src); err != nil {
61 | 		t.Errorf("Error while merging %s", err)
62 | 	}
63 | 
64 | 	if !dst.Created.Equal(src.Created) {
65 | 		t.Errorf("Created not merged in properly: dst.Created(%v) != src.Created(%v)", dst.Created, src.Created)
66 | 	}
67 | }
68 | 


--------------------------------------------------------------------------------
/issue50_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 	"time"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type testStruct struct {
11 | 	time.Duration
12 | }
13 | 
14 | func TestIssue50Merge(t *testing.T) {
15 | 	to := testStruct{}
16 | 	from := testStruct{}
17 | 
18 | 	if err := mergo.Merge(&to, from); err != nil {
19 | 		t.Fail()
20 | 	}
21 | }
22 | 


--------------------------------------------------------------------------------
/issue52_test.go:
--------------------------------------------------------------------------------
  1 | package mergo_test
  2 | 
  3 | import (
  4 | 	"reflect"
  5 | 	"testing"
  6 | 	"time"
  7 | 
  8 | 	"dario.cat/mergo"
  9 | )
 10 | 
 11 | type structWithTime struct {
 12 | 	Birth time.Time
 13 | }
 14 | 
 15 | type timeTransfomer struct {
 16 | 	overwrite bool
 17 | }
 18 | 
 19 | func (t timeTransfomer) Transformer(typ reflect.Type) func(dst, src reflect.Value) error {
 20 | 	if typ == reflect.TypeOf(time.Time{}) {
 21 | 		return func(dst, src reflect.Value) error {
 22 | 			if dst.CanSet() {
 23 | 				if t.overwrite {
 24 | 					isZero := src.MethodByName("IsZero")
 25 | 
 26 | 					result := isZero.Call([]reflect.Value{})
 27 | 					if !result[0].Bool() {
 28 | 						dst.Set(src)
 29 | 					}
 30 | 				} else {
 31 | 					isZero := dst.MethodByName("IsZero")
 32 | 
 33 | 					result := isZero.Call([]reflect.Value{})
 34 | 					if result[0].Bool() {
 35 | 						dst.Set(src)
 36 | 					}
 37 | 				}
 38 | 			}
 39 | 			return nil
 40 | 		}
 41 | 	}
 42 | 	return nil
 43 | }
 44 | 
 45 | func TestOverwriteZeroSrcTime(t *testing.T) {
 46 | 	now := time.Now()
 47 | 	dst := structWithTime{now}
 48 | 	src := structWithTime{}
 49 | 
 50 | 	if err := mergo.MergeWithOverwrite(&dst, src); err != nil {
 51 | 		t.FailNow()
 52 | 	}
 53 | 
 54 | 	if !dst.Birth.IsZero() {
 55 | 		t.Errorf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
 56 | 	}
 57 | }
 58 | 
 59 | func TestOverwriteZeroSrcTimeWithTransformer(t *testing.T) {
 60 | 	now := time.Now()
 61 | 	dst := structWithTime{now}
 62 | 	src := structWithTime{}
 63 | 
 64 | 	if err := mergo.MergeWithOverwrite(&dst, src, mergo.WithTransformers(timeTransfomer{true})); err != nil {
 65 | 		t.FailNow()
 66 | 	}
 67 | 
 68 | 	if dst.Birth.IsZero() {
 69 | 		t.Errorf("dst should not have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
 70 | 	}
 71 | }
 72 | 
 73 | func TestOverwriteZeroDstTime(t *testing.T) {
 74 | 	now := time.Now()
 75 | 	dst := structWithTime{}
 76 | 	src := structWithTime{now}
 77 | 
 78 | 	if err := mergo.MergeWithOverwrite(&dst, src); err != nil {
 79 | 		t.FailNow()
 80 | 	}
 81 | 
 82 | 	if dst.Birth.IsZero() {
 83 | 		t.Errorf("dst should have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{})
 84 | 	}
 85 | }
 86 | 
 87 | func TestZeroDstTime(t *testing.T) {
 88 | 	now := time.Now()
 89 | 	dst := structWithTime{}
 90 | 	src := structWithTime{now}
 91 | 
 92 | 	if err := mergo.Merge(&dst, src); err != nil {
 93 | 		t.FailNow()
 94 | 	}
 95 | 
 96 | 	if !dst.Birth.IsZero() {
 97 | 		t.Errorf("dst should not have been overwritten: dst.Birth(%v) != zero(%v)", dst.Birth, time.Time{})
 98 | 	}
 99 | }
100 | 
101 | func TestZeroDstTimeWithTransformer(t *testing.T) {
102 | 	now := time.Now()
103 | 	dst := structWithTime{}
104 | 	src := structWithTime{now}
105 | 
106 | 	if err := mergo.Merge(&dst, src, mergo.WithTransformers(timeTransfomer{})); err != nil {
107 | 		t.FailNow()
108 | 	}
109 | 
110 | 	if dst.Birth.IsZero() {
111 | 		t.Errorf("dst should have been overwritten: dst.Birth(%v) != now(%v)", dst.Birth, now)
112 | 	}
113 | }
114 | 


--------------------------------------------------------------------------------
/issue61_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | func TestIssue61MergeNilMap(t *testing.T) {
11 | 	type T struct {
12 | 		I map[string][]string
13 | 	}
14 | 	t1 := T{}
15 | 	t2 := T{I: map[string][]string{"hi": {"there"}}}
16 | 
17 | 	if err := mergo.Merge(&t1, t2); err != nil {
18 | 		t.Fail()
19 | 	}
20 | 
21 | 	if !reflect.DeepEqual(t2, T{I: map[string][]string{"hi": {"there"}}}) {
22 | 		t.FailNow()
23 | 	}
24 | }
25 | 


--------------------------------------------------------------------------------
/issue64_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type Student struct {
10 | 	Name  string
11 | 	Books []string
12 | }
13 | 
14 | type issue64TestData struct {
15 | 	S1            Student
16 | 	S2            Student
17 | 	ExpectedSlice []string
18 | }
19 | 
20 | func issue64Data() []issue64TestData {
21 | 	return []issue64TestData{
22 | 		{Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"a", "B"}},
23 | 		{Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}},
24 | 		{Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}},
25 | 		{Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}},
26 | 	}
27 | }
28 | 
29 | func TestIssue64MergeSliceWithOverride(t *testing.T) {
30 | 	for _, data := range issue64Data() {
31 | 		err := mergo.Merge(&data.S2, data.S1, mergo.WithOverride)
32 | 		if err != nil {
33 | 			t.Errorf("Error while merging %s", err)
34 | 		}
35 | 
36 | 		if len(data.S2.Books) != len(data.ExpectedSlice) {
37 | 			t.Errorf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice))
38 | 		}
39 | 
40 | 		for i, val := range data.S2.Books {
41 | 			if val != data.ExpectedSlice[i] {
42 | 				t.Errorf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val)
43 | 			}
44 | 		}
45 | 	}
46 | }
47 | 


--------------------------------------------------------------------------------
/issue66_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type PrivateSliceTest66 struct {
10 | 	PublicStrings  []string
11 | 	privateStrings []string
12 | }
13 | 
14 | func TestPrivateSlice(t *testing.T) {
15 | 	p1 := PrivateSliceTest66{
16 | 		PublicStrings:  []string{"one", "two", "three"},
17 | 		privateStrings: []string{"four", "five"},
18 | 	}
19 | 	p2 := PrivateSliceTest66{
20 | 		PublicStrings: []string{"six", "seven"},
21 | 	}
22 | 
23 | 	if err := mergo.Merge(&p1, p2); err != nil {
24 | 		t.Errorf("Error during the merge: %v", err)
25 | 	}
26 | 
27 | 	if len(p1.PublicStrings) != 3 {
28 | 		t.Error("3 elements should be in 'PublicStrings' field, when no append")
29 | 	}
30 | 
31 | 	if len(p1.privateStrings) != 2 {
32 | 		t.Error("2 elements should be in 'privateStrings' field")
33 | 	}
34 | }
35 | 
36 | func TestPrivateSliceWithAppendSlice(t *testing.T) {
37 | 	p1 := PrivateSliceTest66{
38 | 		PublicStrings:  []string{"one", "two", "three"},
39 | 		privateStrings: []string{"four", "five"},
40 | 	}
41 | 	p2 := PrivateSliceTest66{
42 | 		PublicStrings: []string{"six", "seven"},
43 | 	}
44 | 
45 | 	if err := mergo.Merge(&p1, p2, mergo.WithAppendSlice); err != nil {
46 | 		t.Errorf("Error during the merge: %v", err)
47 | 	}
48 | 
49 | 	if len(p1.PublicStrings) != 5 {
50 | 		t.Error("5 elements should be in 'PublicStrings' field")
51 | 	}
52 | 
53 | 	if len(p1.privateStrings) != 2 {
54 | 		t.Error("2 elements should be in 'privateStrings' field")
55 | 	}
56 | }
57 | 


--------------------------------------------------------------------------------
/issue83_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type issue83My struct {
10 | 	Data []int
11 | }
12 | 
13 | func TestIssue83(t *testing.T) {
14 | 	dst := issue83My{Data: []int{1, 2, 3}}
15 | 	new := issue83My{}
16 | 	if err := mergo.Merge(&dst, new, mergo.WithOverwriteWithEmptyValue); err != nil {
17 | 		t.Error(err)
18 | 	}
19 | 	if len(dst.Data) > 0 {
20 | 		t.Errorf("expected empty slice, got %v", dst.Data)
21 | 	}
22 | }
23 | 


--------------------------------------------------------------------------------
/issue84_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type DstStructIssue84 struct {
10 | 	A int
11 | 	B int
12 | 	C int
13 | }
14 | 
15 | type DstNestedStructIssue84 struct {
16 | 	A struct {
17 | 		A int
18 | 		B int
19 | 		C int
20 | 	}
21 | 	B int
22 | 	C int
23 | }
24 | 
25 | func TestIssue84MergeMapWithNilValueToStructWithOverride(t *testing.T) {
26 | 	p1 := DstStructIssue84{
27 | 		A: 0, B: 1, C: 2,
28 | 	}
29 | 	p2 := map[string]interface{}{
30 | 		"A": 3, "B": 4, "C": 0,
31 | 	}
32 | 
33 | 	if err := mergo.Map(&p1, p2, mergo.WithOverride); err != nil {
34 | 		t.Errorf("Error during the merge: %v", err)
35 | 	}
36 | 
37 | 	if p1.C != 0 {
38 | 		t.Error("C field should become '0'")
39 | 	}
40 | }
41 | 
42 | func TestIssue84MergeMapWithoutKeyExistsToStructWithOverride(t *testing.T) {
43 | 	p1 := DstStructIssue84{
44 | 		A: 0, B: 1, C: 2,
45 | 	}
46 | 	p2 := map[string]interface{}{
47 | 		"A": 3, "B": 4,
48 | 	}
49 | 
50 | 	if err := mergo.Map(&p1, p2, mergo.WithOverride); err != nil {
51 | 		t.Errorf("Error during the merge: %v", err)
52 | 	}
53 | 
54 | 	if p1.C != 2 {
55 | 		t.Error("C field should be '2'")
56 | 	}
57 | }
58 | 
59 | func TestIssue84MergeNestedMapWithNilValueToStructWithOverride(t *testing.T) {
60 | 	p1 := DstNestedStructIssue84{
61 | 		A: struct {
62 | 			A int
63 | 			B int
64 | 			C int
65 | 		}{A: 1, B: 2, C: 0},
66 | 		B: 0,
67 | 		C: 2,
68 | 	}
69 | 	p2 := map[string]interface{}{
70 | 		"A": map[string]interface{}{
71 | 			"A": 0, "B": 0, "C": 5,
72 | 		}, "B": 4, "C": 0,
73 | 	}
74 | 
75 | 	if err := mergo.Map(&p1, p2, mergo.WithOverride); err != nil {
76 | 		t.Errorf("Error during the merge: %v", err)
77 | 	}
78 | 
79 | 	if p1.B != 4 {
80 | 		t.Error("A.C field should become '4'")
81 | 	}
82 | 
83 | 	if p1.A.C != 5 {
84 | 		t.Error("A.C field should become '5'")
85 | 	}
86 | 
87 | 	if p1.A.B != 0 || p1.A.A != 0 {
88 | 		t.Error("A.A and A.B field should become '0'")
89 | 	}
90 | }
91 | 


--------------------------------------------------------------------------------
/issue89_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func TestIssue89Boolean(t *testing.T) {
10 | 	type Foo struct {
11 | 		Bar bool `json:"bar"`
12 | 	}
13 | 
14 | 	src := Foo{Bar: true}
15 | 	dst := Foo{Bar: false}
16 | 
17 | 	if err := mergo.Merge(&dst, src); err != nil {
18 | 		t.Error(err)
19 | 	}
20 | 	if dst.Bar == false {
21 | 		t.Errorf("expected true, got false")
22 | 	}
23 | }
24 | 
25 | func TestIssue89MergeWithEmptyValue(t *testing.T) {
26 | 	p1 := map[string]interface{}{
27 | 		"A": 3, "B": "note", "C": true,
28 | 	}
29 | 	p2 := map[string]interface{}{
30 | 		"B": "", "C": false,
31 | 	}
32 | 	if err := mergo.Merge(&p1, p2, mergo.WithOverwriteWithEmptyValue); err != nil {
33 | 		t.Error(err)
34 | 	}
35 | 	testCases := []struct {
36 | 		expected interface{}
37 | 		key      string
38 | 	}{
39 | 		{
40 | 			"",
41 | 			"B",
42 | 		},
43 | 		{
44 | 			false,
45 | 			"C",
46 | 		},
47 | 	}
48 | 	for _, tC := range testCases {
49 | 		if p1[tC.key] != tC.expected {
50 | 			t.Errorf("expected %v in p1[%q], got %v", tC.expected, tC.key, p1[tC.key])
51 | 		}
52 | 	}
53 | }
54 | 


--------------------------------------------------------------------------------
/issue90_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type structWithStringMap struct {
11 | 	Data map[string]string
12 | }
13 | 
14 | func TestIssue90(t *testing.T) {
15 | 	dst := map[string]structWithStringMap{
16 | 		"struct": {
17 | 			Data: nil,
18 | 		},
19 | 	}
20 | 	src := map[string]structWithStringMap{
21 | 		"struct": {
22 | 			Data: map[string]string{
23 | 				"foo": "bar",
24 | 			},
25 | 		},
26 | 	}
27 | 	expected := map[string]structWithStringMap{
28 | 		"struct": {
29 | 			Data: map[string]string{
30 | 				"foo": "bar",
31 | 			},
32 | 		},
33 | 	}
34 | 
35 | 	err := mergo.Merge(&dst, src, mergo.WithOverride)
36 | 	if err != nil {
37 | 		t.Errorf("unexpected error %v", err)
38 | 	}
39 | 
40 | 	if !reflect.DeepEqual(dst, expected) {
41 | 		t.Errorf("expected: %#v\ngot: %#v", expected, dst)
42 | 	}
43 | }
44 | 


--------------------------------------------------------------------------------
/issueXXX_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | var testDataS = []struct {
10 | 	S1            Student
11 | 	S2            Student
12 | 	ExpectedSlice []string
13 | }{
14 | 	{Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{"1"}}, []string{"1", "a", "B"}},
15 | 	{Student{"Jack", []string{"a", "B"}}, Student{"Tom", []string{}}, []string{"a", "B"}},
16 | 	{Student{"Jack", []string{}}, Student{"Tom", []string{"1"}}, []string{"1"}},
17 | 	{Student{"Jack", []string{}}, Student{"Tom", []string{}}, []string{}},
18 | }
19 | 
20 | func TestMergeSliceWithOverrideWithAppendSlice(t *testing.T) {
21 | 	for _, data := range testDataS {
22 | 		err := mergo.Merge(&data.S2, data.S1, mergo.WithOverride, mergo.WithAppendSlice)
23 | 		if err != nil {
24 | 			t.Errorf("Error while merging %s", err)
25 | 		}
26 | 
27 | 		if len(data.S2.Books) != len(data.ExpectedSlice) {
28 | 			t.Errorf("Got %d elements in slice, but expected %d", len(data.S2.Books), len(data.ExpectedSlice))
29 | 		}
30 | 
31 | 		for i, val := range data.S2.Books {
32 | 			if val != data.ExpectedSlice[i] {
33 | 				t.Errorf("Expected %s, but got %s while merging slice with override", data.ExpectedSlice[i], val)
34 | 			}
35 | 		}
36 | 	}
37 | }
38 | 


--------------------------------------------------------------------------------
/map.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2014 Dario Castañé. All rights reserved.
  2 | // Copyright 2009 The Go Authors. All rights reserved.
  3 | // Use of this source code is governed by a BSD-style
  4 | // license that can be found in the LICENSE file.
  5 | 
  6 | // Based on src/pkg/reflect/deepequal.go from official
  7 | // golang's stdlib.
  8 | 
  9 | package mergo
 10 | 
 11 | import (
 12 | 	"fmt"
 13 | 	"reflect"
 14 | 	"unicode"
 15 | 	"unicode/utf8"
 16 | )
 17 | 
 18 | func changeInitialCase(s string, mapper func(rune) rune) string {
 19 | 	if s == "" {
 20 | 		return s
 21 | 	}
 22 | 	r, n := utf8.DecodeRuneInString(s)
 23 | 	return string(mapper(r)) + s[n:]
 24 | }
 25 | 
 26 | func isExported(field reflect.StructField) bool {
 27 | 	r, _ := utf8.DecodeRuneInString(field.Name)
 28 | 	return r >= 'A' && r <= 'Z'
 29 | }
 30 | 
 31 | // Traverses recursively both values, assigning src's fields values to dst.
 32 | // The map argument tracks comparisons that have already been seen, which allows
 33 | // short circuiting on recursive types.
 34 | func deepMap(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
 35 | 	overwrite := config.Overwrite
 36 | 	if dst.CanAddr() {
 37 | 		addr := dst.UnsafeAddr()
 38 | 		h := 17 * addr
 39 | 		seen := visited[h]
 40 | 		typ := dst.Type()
 41 | 		for p := seen; p != nil; p = p.next {
 42 | 			if p.ptr == addr && p.typ == typ {
 43 | 				return nil
 44 | 			}
 45 | 		}
 46 | 		// Remember, remember...
 47 | 		visited[h] = &visit{typ, seen, addr}
 48 | 	}
 49 | 	zeroValue := reflect.Value{}
 50 | 	switch dst.Kind() {
 51 | 	case reflect.Map:
 52 | 		dstMap := dst.Interface().(map[string]interface{})
 53 | 		for i, n := 0, src.NumField(); i < n; i++ {
 54 | 			srcType := src.Type()
 55 | 			field := srcType.Field(i)
 56 | 			if !isExported(field) {
 57 | 				continue
 58 | 			}
 59 | 			fieldName := field.Name
 60 | 			fieldName = changeInitialCase(fieldName, unicode.ToLower)
 61 | 			if _, ok := dstMap[fieldName]; !ok || (!isEmptyValue(reflect.ValueOf(src.Field(i).Interface()), !config.ShouldNotDereference) && overwrite) || config.overwriteWithEmptyValue {
 62 | 				dstMap[fieldName] = src.Field(i).Interface()
 63 | 			}
 64 | 		}
 65 | 	case reflect.Ptr:
 66 | 		if dst.IsNil() {
 67 | 			v := reflect.New(dst.Type().Elem())
 68 | 			dst.Set(v)
 69 | 		}
 70 | 		dst = dst.Elem()
 71 | 		fallthrough
 72 | 	case reflect.Struct:
 73 | 		srcMap := src.Interface().(map[string]interface{})
 74 | 		for key := range srcMap {
 75 | 			config.overwriteWithEmptyValue = true
 76 | 			srcValue := srcMap[key]
 77 | 			fieldName := changeInitialCase(key, unicode.ToUpper)
 78 | 			dstElement := dst.FieldByName(fieldName)
 79 | 			if dstElement == zeroValue {
 80 | 				// We discard it because the field doesn't exist.
 81 | 				continue
 82 | 			}
 83 | 			srcElement := reflect.ValueOf(srcValue)
 84 | 			dstKind := dstElement.Kind()
 85 | 			srcKind := srcElement.Kind()
 86 | 			if srcKind == reflect.Ptr && dstKind != reflect.Ptr {
 87 | 				srcElement = srcElement.Elem()
 88 | 				srcKind = reflect.TypeOf(srcElement.Interface()).Kind()
 89 | 			} else if dstKind == reflect.Ptr {
 90 | 				// Can this work? I guess it can't.
 91 | 				if srcKind != reflect.Ptr && srcElement.CanAddr() {
 92 | 					srcPtr := srcElement.Addr()
 93 | 					srcElement = reflect.ValueOf(srcPtr)
 94 | 					srcKind = reflect.Ptr
 95 | 				}
 96 | 			}
 97 | 
 98 | 			if !srcElement.IsValid() {
 99 | 				continue
100 | 			}
101 | 			if srcKind == dstKind {
102 | 				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
103 | 					return
104 | 				}
105 | 			} else if dstKind == reflect.Interface && dstElement.Kind() == reflect.Interface {
106 | 				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
107 | 					return
108 | 				}
109 | 			} else if srcKind == reflect.Map {
110 | 				if err = deepMap(dstElement, srcElement, visited, depth+1, config); err != nil {
111 | 					return
112 | 				}
113 | 			} else {
114 | 				return fmt.Errorf("type mismatch on %s field: found %v, expected %v", fieldName, srcKind, dstKind)
115 | 			}
116 | 		}
117 | 	}
118 | 	return
119 | }
120 | 
121 | // Map sets fields' values in dst from src.
122 | // src can be a map with string keys or a struct. dst must be the opposite:
123 | // if src is a map, dst must be a valid pointer to struct. If src is a struct,
124 | // dst must be map[string]interface{}.
125 | // It won't merge unexported (private) fields and will do recursively
126 | // any exported field.
127 | // If dst is a map, keys will be src fields' names in lower camel case.
128 | // Missing key in src that doesn't match a field in dst will be skipped. This
129 | // doesn't apply if dst is a map.
130 | // This is separated method from Merge because it is cleaner and it keeps sane
131 | // semantics: merging equal types, mapping different (restricted) types.
132 | func Map(dst, src interface{}, opts ...func(*Config)) error {
133 | 	return _map(dst, src, opts...)
134 | }
135 | 
136 | // MapWithOverwrite will do the same as Map except that non-empty dst attributes will be overridden by
137 | // non-empty src attribute values.
138 | // Deprecated: Use Map(…) with WithOverride
139 | func MapWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
140 | 	return _map(dst, src, append(opts, WithOverride)...)
141 | }
142 | 
143 | func _map(dst, src interface{}, opts ...func(*Config)) error {
144 | 	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
145 | 		return ErrNonPointerArgument
146 | 	}
147 | 	var (
148 | 		vDst, vSrc reflect.Value
149 | 		err        error
150 | 	)
151 | 	config := &Config{}
152 | 
153 | 	for _, opt := range opts {
154 | 		opt(config)
155 | 	}
156 | 
157 | 	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
158 | 		return err
159 | 	}
160 | 	// To be friction-less, we redirect equal-type arguments
161 | 	// to deepMerge. Only because arguments can be anything.
162 | 	if vSrc.Kind() == vDst.Kind() {
163 | 		return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
164 | 	}
165 | 	switch vSrc.Kind() {
166 | 	case reflect.Struct:
167 | 		if vDst.Kind() != reflect.Map {
168 | 			return ErrExpectedMapAsDestination
169 | 		}
170 | 	case reflect.Map:
171 | 		if vDst.Kind() != reflect.Struct {
172 | 			return ErrExpectedStructAsDestination
173 | 		}
174 | 	default:
175 | 		return ErrNotSupported
176 | 	}
177 | 	return deepMap(vDst, vSrc, make(map[uintptr]*visit), 0, config)
178 | }
179 | 


--------------------------------------------------------------------------------
/merge.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2013 Dario Castañé. All rights reserved.
  2 | // Copyright 2009 The Go Authors. All rights reserved.
  3 | // Use of this source code is governed by a BSD-style
  4 | // license that can be found in the LICENSE file.
  5 | 
  6 | // Based on src/pkg/reflect/deepequal.go from official
  7 | // golang's stdlib.
  8 | 
  9 | package mergo
 10 | 
 11 | import (
 12 | 	"fmt"
 13 | 	"reflect"
 14 | )
 15 | 
 16 | func hasMergeableFields(dst reflect.Value) (exported bool) {
 17 | 	for i, n := 0, dst.NumField(); i < n; i++ {
 18 | 		field := dst.Type().Field(i)
 19 | 		if field.Anonymous && dst.Field(i).Kind() == reflect.Struct {
 20 | 			exported = exported || hasMergeableFields(dst.Field(i))
 21 | 		} else if isExportedComponent(&field) {
 22 | 			exported = exported || len(field.PkgPath) == 0
 23 | 		}
 24 | 	}
 25 | 	return
 26 | }
 27 | 
 28 | func isExportedComponent(field *reflect.StructField) bool {
 29 | 	pkgPath := field.PkgPath
 30 | 	if len(pkgPath) > 0 {
 31 | 		return false
 32 | 	}
 33 | 	c := field.Name[0]
 34 | 	if 'a' <= c && c <= 'z' || c == '_' {
 35 | 		return false
 36 | 	}
 37 | 	return true
 38 | }
 39 | 
 40 | type Config struct {
 41 | 	Transformers                 Transformers
 42 | 	Overwrite                    bool
 43 | 	ShouldNotDereference         bool
 44 | 	AppendSlice                  bool
 45 | 	TypeCheck                    bool
 46 | 	overwriteWithEmptyValue      bool
 47 | 	overwriteSliceWithEmptyValue bool
 48 | 	sliceDeepCopy                bool
 49 | }
 50 | 
 51 | type Transformers interface {
 52 | 	Transformer(reflect.Type) func(dst, src reflect.Value) error
 53 | }
 54 | 
 55 | // Traverses recursively both values, assigning src's fields values to dst.
 56 | // The map argument tracks comparisons that have already been seen, which allows
 57 | // short circuiting on recursive types.
 58 | func deepMerge(dst, src reflect.Value, visited map[uintptr]*visit, depth int, config *Config) (err error) {
 59 | 	overwrite := config.Overwrite
 60 | 	typeCheck := config.TypeCheck
 61 | 	overwriteWithEmptySrc := config.overwriteWithEmptyValue
 62 | 	overwriteSliceWithEmptySrc := config.overwriteSliceWithEmptyValue
 63 | 	sliceDeepCopy := config.sliceDeepCopy
 64 | 
 65 | 	if !src.IsValid() {
 66 | 		return
 67 | 	}
 68 | 	if dst.CanAddr() {
 69 | 		addr := dst.UnsafeAddr()
 70 | 		h := 17 * addr
 71 | 		seen := visited[h]
 72 | 		typ := dst.Type()
 73 | 		for p := seen; p != nil; p = p.next {
 74 | 			if p.ptr == addr && p.typ == typ {
 75 | 				return nil
 76 | 			}
 77 | 		}
 78 | 		// Remember, remember...
 79 | 		visited[h] = &visit{typ, seen, addr}
 80 | 	}
 81 | 
 82 | 	if config.Transformers != nil && !isReflectNil(dst) && dst.IsValid() {
 83 | 		if fn := config.Transformers.Transformer(dst.Type()); fn != nil {
 84 | 			err = fn(dst, src)
 85 | 			return
 86 | 		}
 87 | 	}
 88 | 
 89 | 	switch dst.Kind() {
 90 | 	case reflect.Struct:
 91 | 		if hasMergeableFields(dst) {
 92 | 			for i, n := 0, dst.NumField(); i < n; i++ {
 93 | 				if err = deepMerge(dst.Field(i), src.Field(i), visited, depth+1, config); err != nil {
 94 | 					return
 95 | 				}
 96 | 			}
 97 | 		} else {
 98 | 			if dst.CanSet() && (isReflectNil(dst) || overwrite) && (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc) {
 99 | 				dst.Set(src)
100 | 			}
101 | 		}
102 | 	case reflect.Map:
103 | 		if dst.IsNil() && !src.IsNil() {
104 | 			if dst.CanSet() {
105 | 				dst.Set(reflect.MakeMap(dst.Type()))
106 | 			} else {
107 | 				return
108 | 			}
109 | 		}
110 | 
111 | 		if src.Kind() != reflect.Map {
112 | 			if overwrite && dst.CanSet() {
113 | 				dst.Set(src)
114 | 			}
115 | 			return
116 | 		}
117 | 
118 | 		for _, key := range src.MapKeys() {
119 | 			srcElement := src.MapIndex(key)
120 | 			if !srcElement.IsValid() {
121 | 				continue
122 | 			}
123 | 			dstElement := dst.MapIndex(key)
124 | 			switch srcElement.Kind() {
125 | 			case reflect.Chan, reflect.Func, reflect.Map, reflect.Interface, reflect.Slice:
126 | 				if srcElement.IsNil() {
127 | 					if overwrite {
128 | 						dst.SetMapIndex(key, srcElement)
129 | 					}
130 | 					continue
131 | 				}
132 | 				fallthrough
133 | 			default:
134 | 				if !srcElement.CanInterface() {
135 | 					continue
136 | 				}
137 | 				switch reflect.TypeOf(srcElement.Interface()).Kind() {
138 | 				case reflect.Struct:
139 | 					fallthrough
140 | 				case reflect.Ptr:
141 | 					fallthrough
142 | 				case reflect.Map:
143 | 					srcMapElm := srcElement
144 | 					dstMapElm := dstElement
145 | 					if srcMapElm.CanInterface() {
146 | 						srcMapElm = reflect.ValueOf(srcMapElm.Interface())
147 | 						if dstMapElm.IsValid() {
148 | 							dstMapElm = reflect.ValueOf(dstMapElm.Interface())
149 | 						}
150 | 					}
151 | 					if err = deepMerge(dstMapElm, srcMapElm, visited, depth+1, config); err != nil {
152 | 						return
153 | 					}
154 | 				case reflect.Slice:
155 | 					srcSlice := reflect.ValueOf(srcElement.Interface())
156 | 
157 | 					var dstSlice reflect.Value
158 | 					if !dstElement.IsValid() || dstElement.IsNil() {
159 | 						dstSlice = reflect.MakeSlice(srcSlice.Type(), 0, srcSlice.Len())
160 | 					} else {
161 | 						dstSlice = reflect.ValueOf(dstElement.Interface())
162 | 					}
163 | 
164 | 					if (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) && !config.AppendSlice && !sliceDeepCopy {
165 | 						if typeCheck && srcSlice.Type() != dstSlice.Type() {
166 | 							return fmt.Errorf("cannot override two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
167 | 						}
168 | 						dstSlice = srcSlice
169 | 					} else if config.AppendSlice {
170 | 						if srcSlice.Type() != dstSlice.Type() {
171 | 							return fmt.Errorf("cannot append two slices with different type (%s, %s)", srcSlice.Type(), dstSlice.Type())
172 | 						}
173 | 						dstSlice = reflect.AppendSlice(dstSlice, srcSlice)
174 | 					} else if sliceDeepCopy {
175 | 						i := 0
176 | 						for ; i < srcSlice.Len() && i < dstSlice.Len(); i++ {
177 | 							srcElement := srcSlice.Index(i)
178 | 							dstElement := dstSlice.Index(i)
179 | 
180 | 							if srcElement.CanInterface() {
181 | 								srcElement = reflect.ValueOf(srcElement.Interface())
182 | 							}
183 | 							if dstElement.CanInterface() {
184 | 								dstElement = reflect.ValueOf(dstElement.Interface())
185 | 							}
186 | 
187 | 							if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
188 | 								return
189 | 							}
190 | 						}
191 | 
192 | 					}
193 | 					dst.SetMapIndex(key, dstSlice)
194 | 				}
195 | 			}
196 | 
197 | 			if dstElement.IsValid() && !isEmptyValue(dstElement, !config.ShouldNotDereference) {
198 | 				if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Slice {
199 | 					continue
200 | 				}
201 | 				if reflect.TypeOf(srcElement.Interface()).Kind() == reflect.Map && reflect.TypeOf(dstElement.Interface()).Kind() == reflect.Map {
202 | 					continue
203 | 				}
204 | 			}
205 | 
206 | 			if srcElement.IsValid() && ((srcElement.Kind() != reflect.Ptr && overwrite) || !dstElement.IsValid() || isEmptyValue(dstElement, !config.ShouldNotDereference)) {
207 | 				if dst.IsNil() {
208 | 					dst.Set(reflect.MakeMap(dst.Type()))
209 | 				}
210 | 				dst.SetMapIndex(key, srcElement)
211 | 			}
212 | 		}
213 | 
214 | 		// Ensure that all keys in dst are deleted if they are not in src.
215 | 		if overwriteWithEmptySrc {
216 | 			for _, key := range dst.MapKeys() {
217 | 				srcElement := src.MapIndex(key)
218 | 				if !srcElement.IsValid() {
219 | 					dst.SetMapIndex(key, reflect.Value{})
220 | 				}
221 | 			}
222 | 		}
223 | 	case reflect.Slice:
224 | 		if !dst.CanSet() {
225 | 			break
226 | 		}
227 | 		if (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc || overwriteSliceWithEmptySrc) && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) && !config.AppendSlice && !sliceDeepCopy {
228 | 			dst.Set(src)
229 | 		} else if config.AppendSlice {
230 | 			if src.Type() != dst.Type() {
231 | 				return fmt.Errorf("cannot append two slice with different type (%s, %s)", src.Type(), dst.Type())
232 | 			}
233 | 			dst.Set(reflect.AppendSlice(dst, src))
234 | 		} else if sliceDeepCopy {
235 | 			for i := 0; i < src.Len() && i < dst.Len(); i++ {
236 | 				srcElement := src.Index(i)
237 | 				dstElement := dst.Index(i)
238 | 				if srcElement.CanInterface() {
239 | 					srcElement = reflect.ValueOf(srcElement.Interface())
240 | 				}
241 | 				if dstElement.CanInterface() {
242 | 					dstElement = reflect.ValueOf(dstElement.Interface())
243 | 				}
244 | 
245 | 				if err = deepMerge(dstElement, srcElement, visited, depth+1, config); err != nil {
246 | 					return
247 | 				}
248 | 			}
249 | 		}
250 | 	case reflect.Ptr:
251 | 		fallthrough
252 | 	case reflect.Interface:
253 | 		if isReflectNil(src) {
254 | 			if overwriteWithEmptySrc && dst.CanSet() && src.Type().AssignableTo(dst.Type()) {
255 | 				dst.Set(src)
256 | 			}
257 | 			break
258 | 		}
259 | 
260 | 		if src.Kind() != reflect.Interface {
261 | 			if dst.IsNil() || (src.Kind() != reflect.Ptr && overwrite) {
262 | 				if dst.CanSet() && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) {
263 | 					dst.Set(src)
264 | 				}
265 | 			} else if src.Kind() == reflect.Ptr {
266 | 				if !config.ShouldNotDereference {
267 | 					if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
268 | 						return
269 | 					}
270 | 				} else if src.Elem().Kind() != reflect.Struct {
271 | 					if overwriteWithEmptySrc || (overwrite && !src.IsNil()) || dst.IsNil() {
272 | 						dst.Set(src)
273 | 					}
274 | 				}
275 | 			} else if dst.Elem().Type() == src.Type() {
276 | 				if err = deepMerge(dst.Elem(), src, visited, depth+1, config); err != nil {
277 | 					return
278 | 				}
279 | 			} else {
280 | 				return ErrDifferentArgumentsTypes
281 | 			}
282 | 			break
283 | 		}
284 | 
285 | 		if dst.IsNil() || overwrite {
286 | 			if dst.CanSet() && (overwrite || isEmptyValue(dst, !config.ShouldNotDereference)) {
287 | 				dst.Set(src)
288 | 			}
289 | 			break
290 | 		}
291 | 
292 | 		if dst.Elem().Kind() == src.Elem().Kind() {
293 | 			if err = deepMerge(dst.Elem(), src.Elem(), visited, depth+1, config); err != nil {
294 | 				return
295 | 			}
296 | 			break
297 | 		}
298 | 	default:
299 | 		mustSet := (isEmptyValue(dst, !config.ShouldNotDereference) || overwrite) && (!isEmptyValue(src, !config.ShouldNotDereference) || overwriteWithEmptySrc)
300 | 		if mustSet {
301 | 			if dst.CanSet() {
302 | 				dst.Set(src)
303 | 			}
304 | 		}
305 | 	}
306 | 
307 | 	return
308 | }
309 | 
310 | // Merge will fill any empty for value type attributes on the dst struct using corresponding
311 | // src attributes if they themselves are not empty. dst and src must be valid same-type structs
312 | // and dst must be a pointer to struct.
313 | // It won't merge unexported (private) fields and will do recursively any exported field.
314 | func Merge(dst, src interface{}, opts ...func(*Config)) error {
315 | 	return merge(dst, src, opts...)
316 | }
317 | 
318 | // MergeWithOverwrite will do the same as Merge except that non-empty dst attributes will be overridden by
319 | // non-empty src attribute values.
320 | // Deprecated: use Merge(…) with WithOverride
321 | func MergeWithOverwrite(dst, src interface{}, opts ...func(*Config)) error {
322 | 	return merge(dst, src, append(opts, WithOverride)...)
323 | }
324 | 
325 | // WithTransformers adds transformers to merge, allowing to customize the merging of some types.
326 | func WithTransformers(transformers Transformers) func(*Config) {
327 | 	return func(config *Config) {
328 | 		config.Transformers = transformers
329 | 	}
330 | }
331 | 
332 | // WithOverride will make merge override non-empty dst attributes with non-empty src attributes values.
333 | func WithOverride(config *Config) {
334 | 	config.Overwrite = true
335 | }
336 | 
337 | // WithOverwriteWithEmptyValue will make merge override non empty dst attributes with empty src attributes values.
338 | func WithOverwriteWithEmptyValue(config *Config) {
339 | 	config.Overwrite = true
340 | 	config.overwriteWithEmptyValue = true
341 | }
342 | 
343 | // WithOverrideEmptySlice will make merge override empty dst slice with empty src slice.
344 | func WithOverrideEmptySlice(config *Config) {
345 | 	config.overwriteSliceWithEmptyValue = true
346 | }
347 | 
348 | // WithoutDereference prevents dereferencing pointers when evaluating whether they are empty
349 | // (i.e. a non-nil pointer is never considered empty).
350 | func WithoutDereference(config *Config) {
351 | 	config.ShouldNotDereference = true
352 | }
353 | 
354 | // WithAppendSlice will make merge append slices instead of overwriting it.
355 | func WithAppendSlice(config *Config) {
356 | 	config.AppendSlice = true
357 | }
358 | 
359 | // WithTypeCheck will make merge check types while overwriting it (must be used with WithOverride).
360 | func WithTypeCheck(config *Config) {
361 | 	config.TypeCheck = true
362 | }
363 | 
364 | // WithSliceDeepCopy will merge slice element one by one with Overwrite flag.
365 | func WithSliceDeepCopy(config *Config) {
366 | 	config.sliceDeepCopy = true
367 | 	config.Overwrite = true
368 | }
369 | 
370 | func merge(dst, src interface{}, opts ...func(*Config)) error {
371 | 	if dst != nil && reflect.ValueOf(dst).Kind() != reflect.Ptr {
372 | 		return ErrNonPointerArgument
373 | 	}
374 | 	var (
375 | 		vDst, vSrc reflect.Value
376 | 		err        error
377 | 	)
378 | 
379 | 	config := &Config{} // First allocation
380 | 
381 | 	for _, opt := range opts {
382 | 		opt(config)
383 | 	}
384 | 
385 | 	if vDst, vSrc, err = resolveValues(dst, src); err != nil {
386 | 		return err
387 | 	}
388 | 	if vDst.Type() != vSrc.Type() {
389 | 		return ErrDifferentArgumentsTypes
390 | 	}
391 | 	return deepMerge(vDst, vSrc, make(map[uintptr]*visit), 0, config)
392 | }
393 | 
394 | // IsReflectNil is the reflect value provided nil
395 | func isReflectNil(v reflect.Value) bool {
396 | 	k := v.Kind()
397 | 	switch k {
398 | 	case reflect.Interface, reflect.Slice, reflect.Chan, reflect.Func, reflect.Map, reflect.Ptr:
399 | 		// Both interface and slice are nil if first word is 0.
400 | 		// Both are always bigger than a word; assume flagIndir.
401 | 		return v.IsNil()
402 | 	default:
403 | 		return false
404 | 	}
405 | }
406 | 


--------------------------------------------------------------------------------
/merge_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | type transformer struct {
11 | 	m map[reflect.Type]func(dst, src reflect.Value) error
12 | }
13 | 
14 | func (s *transformer) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
15 | 	if fn, ok := s.m[t]; ok {
16 | 		return fn
17 | 	}
18 | 	return nil
19 | }
20 | 
21 | type foo struct {
22 | 	Bar *bar
23 | 	s   string
24 | }
25 | 
26 | type bar struct {
27 | 	s map[string]string
28 | 	i int
29 | }
30 | 
31 | func TestMergeWithTransformerNilStruct(t *testing.T) {
32 | 	a := foo{s: "foo"}
33 | 	b := foo{Bar: &bar{i: 2, s: map[string]string{"foo": "bar"}}}
34 | 
35 | 	if err := mergo.Merge(&a, &b, mergo.WithOverride, mergo.WithTransformers(&transformer{
36 | 		m: map[reflect.Type]func(dst, src reflect.Value) error{
37 | 			reflect.TypeOf(&bar{}): func(dst, src reflect.Value) error {
38 | 				// Do sthg with Elem
39 | 				t.Log(dst.Elem().FieldByName("i"))
40 | 				t.Log(src.Elem())
41 | 				return nil
42 | 			},
43 | 		},
44 | 	})); err != nil {
45 | 		t.Error(err)
46 | 	}
47 | 
48 | 	if a.s != "foo" {
49 | 		t.Errorf("b not merged in properly: a.s.Value(%s) != expected(%s)", a.s, "foo")
50 | 	}
51 | 
52 | 	if a.Bar == nil {
53 | 		t.Errorf("b not merged in properly: a.Bar shouldn't be nil")
54 | 	}
55 | }
56 | 
57 | func TestMergeNonPointer(t *testing.T) {
58 | 	dst := bar{
59 | 		i: 1,
60 | 	}
61 | 	src := bar{
62 | 		i: 2,
63 | 		s: map[string]string{
64 | 			"a": "1",
65 | 		},
66 | 	}
67 | 	want := mergo.ErrNonPointerArgument
68 | 
69 | 	if got := mergo.Merge(dst, src); got != want {
70 | 		t.Errorf("want: %s, got: %s", want, got)
71 | 	}
72 | }
73 | 
74 | func TestMapNonPointer(t *testing.T) {
75 | 	dst := make(map[string]bar)
76 | 	src := map[string]bar{
77 | 		"a": {
78 | 			i: 2,
79 | 			s: map[string]string{
80 | 				"a": "1",
81 | 			},
82 | 		},
83 | 	}
84 | 	want := mergo.ErrNonPointerArgument
85 | 	if got := mergo.Merge(dst, src); got != want {
86 | 		t.Errorf("want: %s, got: %s", want, got)
87 | 	}
88 | }
89 | 


--------------------------------------------------------------------------------
/mergo.go:
--------------------------------------------------------------------------------
 1 | // Copyright 2013 Dario Castañé. All rights reserved.
 2 | // Copyright 2009 The Go Authors. All rights reserved.
 3 | // Use of this source code is governed by a BSD-style
 4 | // license that can be found in the LICENSE file.
 5 | 
 6 | // Based on src/pkg/reflect/deepequal.go from official
 7 | // golang's stdlib.
 8 | 
 9 | package mergo
10 | 
11 | import (
12 | 	"errors"
13 | 	"reflect"
14 | )
15 | 
16 | // Errors reported by Mergo when it finds invalid arguments.
17 | var (
18 | 	ErrNilArguments                = errors.New("src and dst must not be nil")
19 | 	ErrDifferentArgumentsTypes     = errors.New("src and dst must be of same type")
20 | 	ErrNotSupported                = errors.New("only structs, maps, and slices are supported")
21 | 	ErrExpectedMapAsDestination    = errors.New("dst was expected to be a map")
22 | 	ErrExpectedStructAsDestination = errors.New("dst was expected to be a struct")
23 | 	ErrNonPointerArgument          = errors.New("dst must be a pointer")
24 | )
25 | 
26 | // During deepMerge, must keep track of checks that are
27 | // in progress.  The comparison algorithm assumes that all
28 | // checks in progress are true when it reencounters them.
29 | // Visited are stored in a map indexed by 17 * a1 + a2;
30 | type visit struct {
31 | 	typ  reflect.Type
32 | 	next *visit
33 | 	ptr  uintptr
34 | }
35 | 
36 | // From src/pkg/encoding/json/encode.go.
37 | func isEmptyValue(v reflect.Value, shouldDereference bool) bool {
38 | 	switch v.Kind() {
39 | 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:
40 | 		return v.Len() == 0
41 | 	case reflect.Bool:
42 | 		return !v.Bool()
43 | 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
44 | 		return v.Int() == 0
45 | 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
46 | 		return v.Uint() == 0
47 | 	case reflect.Float32, reflect.Float64:
48 | 		return v.Float() == 0
49 | 	case reflect.Interface, reflect.Ptr:
50 | 		if v.IsNil() {
51 | 			return true
52 | 		}
53 | 		if shouldDereference {
54 | 			return isEmptyValue(v.Elem(), shouldDereference)
55 | 		}
56 | 		return false
57 | 	case reflect.Func:
58 | 		return v.IsNil()
59 | 	case reflect.Invalid:
60 | 		return true
61 | 	}
62 | 	return false
63 | }
64 | 
65 | func resolveValues(dst, src interface{}) (vDst, vSrc reflect.Value, err error) {
66 | 	if dst == nil || src == nil {
67 | 		err = ErrNilArguments
68 | 		return
69 | 	}
70 | 	vDst = reflect.ValueOf(dst).Elem()
71 | 	if vDst.Kind() != reflect.Struct && vDst.Kind() != reflect.Map && vDst.Kind() != reflect.Slice {
72 | 		err = ErrNotSupported
73 | 		return
74 | 	}
75 | 	vSrc = reflect.ValueOf(src)
76 | 	// We check if vSrc is a pointer to dereference it.
77 | 	if vSrc.Kind() == reflect.Ptr {
78 | 		vSrc = vSrc.Elem()
79 | 	}
80 | 	return
81 | }
82 | 


--------------------------------------------------------------------------------
/mergo_test.go:
--------------------------------------------------------------------------------
  1 | // Copyright 2013 Dario Castañé. All rights reserved.
  2 | // Copyright 2009 The Go Authors. All rights reserved.
  3 | // Use of this source code is governed by a BSD-style
  4 | // license that can be found in the LICENSE file.
  5 | 
  6 | package mergo_test
  7 | 
  8 | import (
  9 | 	"encoding/json"
 10 | 	"os"
 11 | 	"reflect"
 12 | 	"strings"
 13 | 	"testing"
 14 | 	"time"
 15 | 
 16 | 	"dario.cat/mergo"
 17 | )
 18 | 
 19 | type simpleTest struct {
 20 | 	Value int
 21 | }
 22 | 
 23 | type complexTest struct {
 24 | 	ID string
 25 | 	St simpleTest
 26 | 	sz int
 27 | }
 28 | 
 29 | type mapTest struct {
 30 | 	M map[int]int
 31 | }
 32 | 
 33 | type ifcTest struct {
 34 | 	I interface{}
 35 | }
 36 | 
 37 | type moreComplextText struct {
 38 | 	Ct complexTest
 39 | 	St simpleTest
 40 | 	Nt simpleTest
 41 | }
 42 | 
 43 | type pointerTest struct {
 44 | 	C *simpleTest
 45 | }
 46 | 
 47 | type sliceTest struct {
 48 | 	S []int
 49 | }
 50 | 
 51 | func TestKb(t *testing.T) {
 52 | 	type testStruct struct {
 53 | 		KeyValue map[string]interface{}
 54 | 		Name     string
 55 | 	}
 56 | 
 57 | 	akv := make(map[string]interface{})
 58 | 	akv["Key1"] = "not value 1"
 59 | 	akv["Key2"] = "value2"
 60 | 	a := testStruct{}
 61 | 	a.Name = "A"
 62 | 	a.KeyValue = akv
 63 | 
 64 | 	bkv := make(map[string]interface{})
 65 | 	bkv["Key1"] = "value1"
 66 | 	bkv["Key3"] = "value3"
 67 | 	b := testStruct{}
 68 | 	b.Name = "B"
 69 | 	b.KeyValue = bkv
 70 | 
 71 | 	ekv := make(map[string]interface{})
 72 | 	ekv["Key1"] = "value1"
 73 | 	ekv["Key2"] = "value2"
 74 | 	ekv["Key3"] = "value3"
 75 | 	expected := testStruct{}
 76 | 	expected.Name = "B"
 77 | 	expected.KeyValue = ekv
 78 | 
 79 | 	if err := mergo.Merge(&b, a); err != nil {
 80 | 		t.Error(err)
 81 | 	}
 82 | 
 83 | 	if !reflect.DeepEqual(b, expected) {
 84 | 		t.Errorf("Actual: %#v did not match \nExpected: %#v", b, expected)
 85 | 	}
 86 | }
 87 | 
 88 | func TestNil(t *testing.T) {
 89 | 	t.Run("both nil", func(t *testing.T) {
 90 | 		if err := mergo.Merge(nil, nil); err != mergo.ErrNilArguments {
 91 | 			t.Fail()
 92 | 		}
 93 | 	})
 94 | 	t.Run("dst nil", func(t *testing.T) {
 95 | 		if err := mergo.Merge(nil, struct{}{}); err != mergo.ErrNilArguments {
 96 | 			t.Fail()
 97 | 		}
 98 | 	})
 99 | 	t.Run("src nil", func(t *testing.T) {
100 | 		dst := struct{}{}
101 | 		if err := mergo.Merge(&dst, nil); err != mergo.ErrNilArguments {
102 | 			t.Error(err)
103 | 		}
104 | 	})
105 | }
106 | 
107 | func TestDifferentTypes(t *testing.T) {
108 | 	a := simpleTest{42}
109 | 	b := 42
110 | 	if err := mergo.Merge(&a, b); err != mergo.ErrDifferentArgumentsTypes {
111 | 		t.Fail()
112 | 	}
113 | }
114 | 
115 | func TestSimpleStruct(t *testing.T) {
116 | 	a := simpleTest{}
117 | 	b := simpleTest{42}
118 | 	if err := mergo.Merge(&a, b); err != nil {
119 | 		t.FailNow()
120 | 	}
121 | 	if a.Value != 42 {
122 | 		t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%d)", a.Value, b.Value)
123 | 	}
124 | 	if !reflect.DeepEqual(a, b) {
125 | 		t.FailNow()
126 | 	}
127 | }
128 | 
129 | func TestComplexStruct(t *testing.T) {
130 | 	a := complexTest{}
131 | 	a.ID = "athing"
132 | 	b := complexTest{"bthing", simpleTest{42}, 1}
133 | 	if err := mergo.Merge(&a, b); err != nil {
134 | 		t.FailNow()
135 | 	}
136 | 	if a.St.Value != 42 {
137 | 		t.Errorf("b not merged in properly: a.St.Value(%d) != b.St.Value(%d)", a.St.Value, b.St.Value)
138 | 	}
139 | 	if a.sz == 1 {
140 | 		t.Errorf("a's private field sz not preserved from merge: a.sz(%d) == b.sz(%d)", a.sz, b.sz)
141 | 	}
142 | 	if a.ID == b.ID {
143 | 		t.Errorf("a's field ID merged unexpectedly: a.ID(%s) == b.ID(%s)", a.ID, b.ID)
144 | 	}
145 | }
146 | 
147 | func TestComplexStructWithOverwrite(t *testing.T) {
148 | 	a := complexTest{"do-not-overwrite-with-empty-value", simpleTest{1}, 1}
149 | 	b := complexTest{"", simpleTest{42}, 2}
150 | 
151 | 	expect := complexTest{"do-not-overwrite-with-empty-value", simpleTest{42}, 1}
152 | 	if err := mergo.MergeWithOverwrite(&a, b); err != nil {
153 | 		t.FailNow()
154 | 	}
155 | 
156 | 	if !reflect.DeepEqual(a, expect) {
157 | 		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", a, expect)
158 | 	}
159 | }
160 | 
161 | func TestPointerStruct(t *testing.T) {
162 | 	s1 := simpleTest{}
163 | 	s2 := simpleTest{19}
164 | 	a := pointerTest{&s1}
165 | 	b := pointerTest{&s2}
166 | 	if err := mergo.Merge(&a, b); err != nil {
167 | 		t.FailNow()
168 | 	}
169 | 	if a.C.Value != b.C.Value {
170 | 		t.Errorf("b not merged in properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
171 | 	}
172 | }
173 | 
174 | type embeddingStruct struct {
175 | 	embeddedStruct
176 | }
177 | 
178 | type embeddedStruct struct {
179 | 	A string
180 | }
181 | 
182 | func TestEmbeddedStruct(t *testing.T) {
183 | 	tests := []struct {
184 | 		src      embeddingStruct
185 | 		dst      embeddingStruct
186 | 		expected embeddingStruct
187 | 	}{
188 | 		{
189 | 			src: embeddingStruct{
190 | 				embeddedStruct{"foo"},
191 | 			},
192 | 			dst: embeddingStruct{
193 | 				embeddedStruct{""},
194 | 			},
195 | 			expected: embeddingStruct{
196 | 				embeddedStruct{"foo"},
197 | 			},
198 | 		},
199 | 		{
200 | 			src: embeddingStruct{
201 | 				embeddedStruct{""},
202 | 			},
203 | 			dst: embeddingStruct{
204 | 				embeddedStruct{"bar"},
205 | 			},
206 | 			expected: embeddingStruct{
207 | 				embeddedStruct{"bar"},
208 | 			},
209 | 		},
210 | 		{
211 | 			src: embeddingStruct{
212 | 				embeddedStruct{"foo"},
213 | 			},
214 | 			dst: embeddingStruct{
215 | 				embeddedStruct{"bar"},
216 | 			},
217 | 			expected: embeddingStruct{
218 | 				embeddedStruct{"bar"},
219 | 			},
220 | 		},
221 | 	}
222 | 
223 | 	for _, test := range tests {
224 | 		err := mergo.Merge(&test.dst, test.src)
225 | 		if err != nil {
226 | 			t.Errorf("unexpected error: %v", err)
227 | 			continue
228 | 		}
229 | 		if !reflect.DeepEqual(test.dst, test.expected) {
230 | 			t.Errorf("unexpected output\nexpected:\n%+v\nsaw:\n%+v\n", test.expected, test.dst)
231 | 		}
232 | 	}
233 | }
234 | 
235 | func TestPointerStructNil(t *testing.T) {
236 | 	a := pointerTest{nil}
237 | 	b := pointerTest{&simpleTest{19}}
238 | 	if err := mergo.Merge(&a, b); err != nil {
239 | 		t.FailNow()
240 | 	}
241 | 	if a.C.Value != b.C.Value {
242 | 		t.Errorf("b not merged in a properly: a.C.Value(%d) != b.C.Value(%d)", a.C.Value, b.C.Value)
243 | 	}
244 | }
245 | 
246 | func testSlice(t *testing.T, a []int, b []int, e []int, opts ...func(*mergo.Config)) {
247 | 	t.Helper()
248 | 	bc := b
249 | 
250 | 	sa := sliceTest{a}
251 | 	sb := sliceTest{b}
252 | 	if err := mergo.Merge(&sa, sb, opts...); err != nil {
253 | 		t.FailNow()
254 | 	}
255 | 	if !reflect.DeepEqual(sb.S, bc) {
256 | 		t.Errorf("Source slice was modified %d != %d", sb.S, bc)
257 | 	}
258 | 	if !reflect.DeepEqual(sa.S, e) {
259 | 		t.Errorf("b not merged in a proper way %d != %d", sa.S, e)
260 | 	}
261 | 
262 | 	ma := map[string][]int{"S": a}
263 | 	mb := map[string][]int{"S": b}
264 | 	if err := mergo.Merge(&ma, mb, opts...); err != nil {
265 | 		t.FailNow()
266 | 	}
267 | 	if !reflect.DeepEqual(mb["S"], bc) {
268 | 		t.Errorf("map value: Source slice was modified %d != %d", mb["S"], bc)
269 | 	}
270 | 	if !reflect.DeepEqual(ma["S"], e) {
271 | 		t.Errorf("map value: b not merged in a proper way %d != %d", ma["S"], e)
272 | 	}
273 | 
274 | 	if a == nil {
275 | 		// test case with missing dst key
276 | 		ma := map[string][]int{}
277 | 		mb := map[string][]int{"S": b}
278 | 		if err := mergo.Merge(&ma, mb); err != nil {
279 | 			t.FailNow()
280 | 		}
281 | 		if !reflect.DeepEqual(mb["S"], bc) {
282 | 			t.Errorf("missing dst key: Source slice was modified %d != %d", mb["S"], bc)
283 | 		}
284 | 		if !reflect.DeepEqual(ma["S"], e) {
285 | 			t.Errorf("missing dst key: b not merged in a proper way %d != %d", ma["S"], e)
286 | 		}
287 | 	}
288 | 
289 | 	if b == nil {
290 | 		// test case with missing src key
291 | 		ma := map[string][]int{"S": a}
292 | 		mb := map[string][]int{}
293 | 		if err := mergo.Merge(&ma, mb); err != nil {
294 | 			t.FailNow()
295 | 		}
296 | 		if !reflect.DeepEqual(mb["S"], bc) {
297 | 			t.Errorf("missing src key: Source slice was modified %d != %d", mb["S"], bc)
298 | 		}
299 | 		if !reflect.DeepEqual(ma["S"], e) {
300 | 			t.Errorf("missing src key: b not merged in a proper way %d != %d", ma["S"], e)
301 | 		}
302 | 	}
303 | }
304 | 
305 | func TestSlice(t *testing.T) {
306 | 	testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3})
307 | 	testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3})
308 | 	testSlice(t, []int{1}, []int{2, 3}, []int{1})
309 | 	testSlice(t, []int{1}, []int{}, []int{1})
310 | 	testSlice(t, []int{1}, nil, []int{1})
311 | 	testSlice(t, nil, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
312 | 	testSlice(t, []int{}, []int{1, 2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
313 | 	testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice)
314 | 	testSlice(t, []int{1}, []int{2, 3}, []int{1, 2, 3}, mergo.WithAppendSlice, mergo.WithOverride)
315 | 	testSlice(t, []int{1}, []int{}, []int{1}, mergo.WithAppendSlice)
316 | 	testSlice(t, []int{1}, nil, []int{1}, mergo.WithAppendSlice)
317 | }
318 | 
319 | func TestEmptyMaps(t *testing.T) {
320 | 	a := mapTest{}
321 | 	b := mapTest{
322 | 		map[int]int{},
323 | 	}
324 | 	if err := mergo.Merge(&a, b); err != nil {
325 | 		t.Fail()
326 | 	}
327 | 	if !reflect.DeepEqual(a, b) {
328 | 		t.FailNow()
329 | 	}
330 | }
331 | 
332 | func TestEmptyToEmptyMaps(t *testing.T) {
333 | 	a := mapTest{}
334 | 	b := mapTest{}
335 | 	if err := mergo.Merge(&a, b); err != nil {
336 | 		t.Fail()
337 | 	}
338 | 	if !reflect.DeepEqual(a, b) {
339 | 		t.FailNow()
340 | 	}
341 | }
342 | 
343 | func TestEmptyToNotEmptyMaps(t *testing.T) {
344 | 	a := mapTest{map[int]int{
345 | 		1: 2,
346 | 		3: 4,
347 | 	}}
348 | 	aa := mapTest{map[int]int{
349 | 		1: 2,
350 | 		3: 4,
351 | 	}}
352 | 	b := mapTest{
353 | 		map[int]int{},
354 | 	}
355 | 	if err := mergo.Merge(&a, b); err != nil {
356 | 		t.Fail()
357 | 	}
358 | 	if !reflect.DeepEqual(a, aa) {
359 | 		t.FailNow()
360 | 	}
361 | }
362 | 
363 | func TestMapsWithOverwrite(t *testing.T) {
364 | 	m := map[string]simpleTest{
365 | 		"a": {},   // overwritten by 16
366 | 		"b": {42}, // overwritten by 0, as map Value is not addressable and it doesn't check for b is set or not set in `n`
367 | 		"c": {13}, // overwritten by 12
368 | 		"d": {61},
369 | 	}
370 | 	n := map[string]simpleTest{
371 | 		"a": {16},
372 | 		"b": {},
373 | 		"c": {12},
374 | 		"e": {14},
375 | 	}
376 | 	expect := map[string]simpleTest{
377 | 		"a": {16},
378 | 		"b": {},
379 | 		"c": {12},
380 | 		"d": {61},
381 | 		"e": {14},
382 | 	}
383 | 
384 | 	if err := mergo.MergeWithOverwrite(&m, n); err != nil {
385 | 		t.Error(err.Error())
386 | 	}
387 | 
388 | 	if !reflect.DeepEqual(m, expect) {
389 | 		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", m, expect)
390 | 	}
391 | }
392 | 
393 | func TestMapWithEmbeddedStructPointer(t *testing.T) {
394 | 	m := map[string]*simpleTest{
395 | 		"a": {},   // overwritten by 16
396 | 		"b": {42}, // not overwritten by empty value
397 | 		"c": {13}, // overwritten by 12
398 | 		"d": {61},
399 | 	}
400 | 	n := map[string]*simpleTest{
401 | 		"a": {16},
402 | 		"b": {},
403 | 		"c": {12},
404 | 		"e": {14},
405 | 	}
406 | 	expect := map[string]*simpleTest{
407 | 		"a": {16},
408 | 		"b": {42},
409 | 		"c": {12},
410 | 		"d": {61},
411 | 		"e": {14},
412 | 	}
413 | 
414 | 	if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil {
415 | 		t.Error(err.Error())
416 | 	}
417 | 
418 | 	if !reflect.DeepEqual(m, expect) {
419 | 		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", m, expect)
420 | 	}
421 | }
422 | 
423 | func TestMergeUsingStructAndMap(t *testing.T) {
424 | 	type multiPtr struct {
425 | 		Text   string
426 | 		Number int
427 | 	}
428 | 	type final struct {
429 | 		Msg1 string
430 | 		Msg2 string
431 | 	}
432 | 	type params struct {
433 | 		Multi *multiPtr
434 | 		Final *final
435 | 		Name  string
436 | 	}
437 | 	type config struct {
438 | 		Params *params
439 | 		Foo    string
440 | 		Bar    string
441 | 	}
442 | 
443 | 	cases := []struct {
444 | 		changes   *config
445 | 		target    *config
446 | 		output    *config
447 | 		name      string
448 | 		overwrite bool
449 | 	}{
450 | 		{
451 | 			name:      "Should overwrite values in target for non-nil values in source",
452 | 			overwrite: true,
453 | 			changes: &config{
454 | 				Bar: "from changes",
455 | 				Params: &params{
456 | 					Final: &final{
457 | 						Msg1: "from changes",
458 | 						Msg2: "from changes",
459 | 					},
460 | 				},
461 | 			},
462 | 			target: &config{
463 | 				Foo: "from target",
464 | 				Params: &params{
465 | 					Name: "from target",
466 | 					Multi: &multiPtr{
467 | 						Text:   "from target",
468 | 						Number: 5,
469 | 					},
470 | 					Final: &final{
471 | 						Msg1: "from target",
472 | 						Msg2: "",
473 | 					},
474 | 				},
475 | 			},
476 | 			output: &config{
477 | 				Foo: "from target",
478 | 				Bar: "from changes",
479 | 				Params: &params{
480 | 					Name: "from target",
481 | 					Multi: &multiPtr{
482 | 						Text:   "from target",
483 | 						Number: 5,
484 | 					},
485 | 					Final: &final{
486 | 						Msg1: "from changes",
487 | 						Msg2: "from changes",
488 | 					},
489 | 				},
490 | 			},
491 | 		},
492 | 		{
493 | 			name:      "Should not overwrite values in target for non-nil values in source",
494 | 			overwrite: false,
495 | 			changes: &config{
496 | 				Bar: "from changes",
497 | 				Params: &params{
498 | 					Final: &final{
499 | 						Msg1: "from changes",
500 | 						Msg2: "from changes",
501 | 					},
502 | 				},
503 | 			},
504 | 			target: &config{
505 | 				Foo: "from target",
506 | 				Params: &params{
507 | 					Name: "from target",
508 | 					Multi: &multiPtr{
509 | 						Text:   "from target",
510 | 						Number: 5,
511 | 					},
512 | 					Final: &final{
513 | 						Msg1: "from target",
514 | 						Msg2: "",
515 | 					},
516 | 				},
517 | 			},
518 | 			output: &config{
519 | 				Foo: "from target",
520 | 				Bar: "from changes",
521 | 				Params: &params{
522 | 					Name: "from target",
523 | 					Multi: &multiPtr{
524 | 						Text:   "from target",
525 | 						Number: 5,
526 | 					},
527 | 					Final: &final{
528 | 						Msg1: "from target",
529 | 						Msg2: "from changes",
530 | 					},
531 | 				},
532 | 			},
533 | 		},
534 | 	}
535 | 
536 | 	for _, tc := range cases {
537 | 		t.Run(tc.name, func(t *testing.T) {
538 | 			var err error
539 | 			if tc.overwrite {
540 | 				err = mergo.Merge(tc.target, *tc.changes, mergo.WithOverride)
541 | 			} else {
542 | 				err = mergo.Merge(tc.target, *tc.changes)
543 | 			}
544 | 			if err != nil {
545 | 				t.Error(err)
546 | 			}
547 | 			if !reflect.DeepEqual(tc.target, tc.output) {
548 | 				t.Errorf("Test failed:\ngot  :\n%+v\n\nwant :\n%+v\n\n", tc.target.Params, tc.output.Params)
549 | 			}
550 | 		})
551 | 	}
552 | }
553 | func TestMaps(t *testing.T) {
554 | 	m := map[string]simpleTest{
555 | 		"a": {},
556 | 		"b": {42},
557 | 		"c": {13},
558 | 		"d": {61},
559 | 	}
560 | 	n := map[string]simpleTest{
561 | 		"a": {16},
562 | 		"b": {},
563 | 		"c": {12},
564 | 		"e": {14},
565 | 	}
566 | 	expect := map[string]simpleTest{
567 | 		"a": {0},
568 | 		"b": {42},
569 | 		"c": {13},
570 | 		"d": {61},
571 | 		"e": {14},
572 | 	}
573 | 
574 | 	if err := mergo.Merge(&m, n); err != nil {
575 | 		t.Error(err.Error())
576 | 	}
577 | 
578 | 	if !reflect.DeepEqual(m, expect) {
579 | 		t.Errorf("Test failed:\ngot  :\n%#v\n\nwant :\n%#v\n\n", m, expect)
580 | 	}
581 | 	if m["a"].Value != 0 {
582 | 		t.Errorf(`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)
583 | 	}
584 | 	if m["b"].Value != 42 {
585 | 		t.Errorf(`n wrongly merged in m: m["b"].Value(%d) != n["b"].Value(%d)`, m["b"].Value, n["b"].Value)
586 | 	}
587 | 	if m["c"].Value != 13 {
588 | 		t.Errorf(`n overwritten in m: m["c"].Value(%d) != n["c"].Value(%d)`, m["c"].Value, n["c"].Value)
589 | 	}
590 | }
591 | 
592 | func TestMapsWithNilPointer(t *testing.T) {
593 | 	m := map[string]*simpleTest{
594 | 		"a": nil,
595 | 		"b": nil,
596 | 	}
597 | 	n := map[string]*simpleTest{
598 | 		"b": nil,
599 | 		"c": nil,
600 | 	}
601 | 	expect := map[string]*simpleTest{
602 | 		"a": nil,
603 | 		"b": nil,
604 | 		"c": nil,
605 | 	}
606 | 
607 | 	if err := mergo.Merge(&m, n, mergo.WithOverride); err != nil {
608 | 		t.Error(err.Error())
609 | 	}
610 | 
611 | 	if !reflect.DeepEqual(m, expect) {
612 | 		t.Errorf("Test failed:\ngot   :\n%#v\n\nwant :\n%#v\n\n", m, expect)
613 | 	}
614 | }
615 | 
616 | func TestJSONMaps(t *testing.T) {
617 | 	thing := loadFixture("testdata/thing.json")
618 | 	license := loadFixture("testdata/license.json")
619 | 	ft := thing["fields"].(map[string]interface{})
620 | 	fl := license["fields"].(map[string]interface{})
621 | 	// license has one extra field (site) and another already existing in thing (author) that Mergo won't override.
622 | 	expectedLength := len(ft) + len(fl) - 1
623 | 	if err := mergo.Merge(&license, thing); err != nil {
624 | 		t.Error(err.Error())
625 | 	}
626 | 	currentLength := len(license["fields"].(map[string]interface{}))
627 | 	if currentLength != expectedLength {
628 | 		t.Errorf(`thing not merged in license properly, license must have %d elements instead of %d`, expectedLength, currentLength)
629 | 	}
630 | 	fields := license["fields"].(map[string]interface{})
631 | 	if _, ok := fields["id"]; !ok {
632 | 		t.Errorf(`thing not merged in license properly, license must have a new id field from thing`)
633 | 	}
634 | }
635 | 
636 | func TestTwoPointerValues(t *testing.T) {
637 | 	a := &simpleTest{}
638 | 	b := &simpleTest{42}
639 | 	if err := mergo.Merge(a, b); err != nil {
640 | 		t.Errorf(`Boom. You crossed the streams: %s`, err)
641 | 	}
642 | }
643 | 
644 | func TestMap(t *testing.T) {
645 | 	a := complexTest{}
646 | 	a.ID = "athing"
647 | 	c := moreComplextText{a, simpleTest{}, simpleTest{}}
648 | 	b := map[string]interface{}{
649 | 		"ct": map[string]interface{}{
650 | 			"st": map[string]interface{}{
651 | 				"value": 42,
652 | 			},
653 | 			"sz": 1,
654 | 			"id": "bthing",
655 | 		},
656 | 		"st": &simpleTest{144}, // Mapping a reference
657 | 		"zt": simpleTest{299},  // Mapping a missing field (zt doesn't exist)
658 | 		"nt": simpleTest{3},
659 | 	}
660 | 	if err := mergo.Map(&c, b); err != nil {
661 | 		t.FailNow()
662 | 	}
663 | 	m := b["ct"].(map[string]interface{})
664 | 	n := m["st"].(map[string]interface{})
665 | 	o := b["st"].(*simpleTest)
666 | 	p := b["nt"].(simpleTest)
667 | 	if c.Ct.St.Value != 42 {
668 | 		t.Errorf("b not merged in properly: c.Ct.St.Value(%d) != b.Ct.St.Value(%d)", c.Ct.St.Value, n["value"])
669 | 	}
670 | 	if c.St.Value != 144 {
671 | 		t.Errorf("b not merged in properly: c.St.Value(%d) != b.St.Value(%d)", c.St.Value, o.Value)
672 | 	}
673 | 	if c.Nt.Value != 3 {
674 | 		t.Errorf("b not merged in properly: c.Nt.Value(%d) != b.Nt.Value(%d)", c.St.Value, p.Value)
675 | 	}
676 | 	if c.Ct.sz == 1 {
677 | 		t.Errorf("a's private field sz not preserved from merge: c.Ct.sz(%d) == b.Ct.sz(%d)", c.Ct.sz, m["sz"])
678 | 	}
679 | 	if c.Ct.ID == m["id"] {
680 | 		t.Errorf("a's field ID merged unexpectedly: c.Ct.ID(%s) == b.Ct.ID(%s)", c.Ct.ID, m["id"])
681 | 	}
682 | }
683 | 
684 | func TestSimpleMap(t *testing.T) {
685 | 	a := simpleTest{}
686 | 	b := map[string]interface{}{
687 | 		"value": 42,
688 | 	}
689 | 	if err := mergo.Map(&a, b); err != nil {
690 | 		t.FailNow()
691 | 	}
692 | 	if a.Value != 42 {
693 | 		t.Errorf("b not merged in properly: a.Value(%d) != b.Value(%v)", a.Value, b["value"])
694 | 	}
695 | }
696 | 
697 | func TestIfcMap(t *testing.T) {
698 | 	a := ifcTest{}
699 | 	b := ifcTest{42}
700 | 	if err := mergo.Map(&a, b); err != nil {
701 | 		t.FailNow()
702 | 	}
703 | 	if a.I != 42 {
704 | 		t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
705 | 	}
706 | 	if !reflect.DeepEqual(a, b) {
707 | 		t.FailNow()
708 | 	}
709 | }
710 | 
711 | func TestIfcMapNoOverwrite(t *testing.T) {
712 | 	a := ifcTest{13}
713 | 	b := ifcTest{42}
714 | 	if err := mergo.Map(&a, b); err != nil {
715 | 		t.FailNow()
716 | 	}
717 | 	if a.I != 13 {
718 | 		t.Errorf("a not left alone: a.I(%d) == b.I(%d)", a.I, b.I)
719 | 	}
720 | }
721 | 
722 | func TestIfcMapWithOverwrite(t *testing.T) {
723 | 	a := ifcTest{13}
724 | 	b := ifcTest{42}
725 | 	if err := mergo.MapWithOverwrite(&a, b); err != nil {
726 | 		t.FailNow()
727 | 	}
728 | 	if a.I != 42 {
729 | 		t.Errorf("b not merged in properly: a.I(%d) != b.I(%d)", a.I, b.I)
730 | 	}
731 | 	if !reflect.DeepEqual(a, b) {
732 | 		t.FailNow()
733 | 	}
734 | }
735 | 
736 | type pointerMapTest struct {
737 | 	B      *simpleTest
738 | 	A      int
739 | 	hidden int
740 | }
741 | 
742 | func TestBackAndForth(t *testing.T) {
743 | 	pt := pointerMapTest{&simpleTest{66}, 42, 1}
744 | 	m := make(map[string]interface{})
745 | 	if err := mergo.Map(&m, pt); err != nil {
746 | 		t.FailNow()
747 | 	}
748 | 	var (
749 | 		v  interface{}
750 | 		ok bool
751 | 	)
752 | 	if v, ok = m["a"]; v.(int) != pt.A || !ok {
753 | 		t.Errorf("pt not merged in properly: m[`a`](%d) != pt.A(%d)", v, pt.A)
754 | 	}
755 | 	if v, ok = m["b"]; !ok {
756 | 		t.Errorf("pt not merged in properly: B is missing in m")
757 | 	}
758 | 	var st *simpleTest
759 | 	if st = v.(*simpleTest); st.Value != 66 {
760 | 		t.Errorf("something went wrong while mapping pt on m, B wasn't copied")
761 | 	}
762 | 	bpt := pointerMapTest{}
763 | 	if err := mergo.Map(&bpt, m); err != nil {
764 | 		t.Error(err)
765 | 	}
766 | 	if bpt.A != pt.A {
767 | 		t.Errorf("pt not merged in properly: bpt.A(%d) != pt.A(%d)", bpt.A, pt.A)
768 | 	}
769 | 	if bpt.hidden == pt.hidden {
770 | 		t.Errorf("pt unexpectedly merged: bpt.hidden(%d) == pt.hidden(%d)", bpt.hidden, pt.hidden)
771 | 	}
772 | 	if bpt.B.Value != pt.B.Value {
773 | 		t.Errorf("pt not merged in properly: bpt.B.Value(%d) != pt.B.Value(%d)", bpt.B.Value, pt.B.Value)
774 | 	}
775 | }
776 | 
777 | func TestEmbeddedPointerUnpacking(t *testing.T) {
778 | 	tests := []struct{ input pointerMapTest }{
779 | 		{pointerMapTest{nil, 42, 1}},
780 | 		{pointerMapTest{&simpleTest{66}, 42, 1}},
781 | 	}
782 | 	newValue := 77
783 | 	m := map[string]interface{}{
784 | 		"b": map[string]interface{}{
785 | 			"value": newValue,
786 | 		},
787 | 	}
788 | 	for _, test := range tests {
789 | 		pt := test.input
790 | 		if err := mergo.MapWithOverwrite(&pt, m); err != nil {
791 | 			t.FailNow()
792 | 		}
793 | 		if pt.B.Value != newValue {
794 | 			t.Errorf("pt not mapped properly: pt.A.Value(%d) != m[`b`][`value`](%d)", pt.B.Value, newValue)
795 | 		}
796 | 
797 | 	}
798 | }
799 | 
800 | type structWithTimePointer struct {
801 | 	Birth *time.Time
802 | }
803 | 
804 | func TestTime(t *testing.T) {
805 | 	now := time.Now()
806 | 	dataStruct := structWithTimePointer{
807 | 		Birth: &now,
808 | 	}
809 | 	dataMap := map[string]interface{}{
810 | 		"Birth": &now,
811 | 	}
812 | 	b := structWithTimePointer{}
813 | 	if err := mergo.Merge(&b, dataStruct); err != nil {
814 | 		t.FailNow()
815 | 	}
816 | 	if b.Birth.IsZero() {
817 | 		t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
818 | 	}
819 | 	if b.Birth != dataStruct.Birth {
820 | 		t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataStruct['Birth'](%v)", b.Birth, dataStruct.Birth)
821 | 	}
822 | 	b = structWithTimePointer{}
823 | 	if err := mergo.Map(&b, dataMap); err != nil {
824 | 		t.FailNow()
825 | 	}
826 | 	if b.Birth.IsZero() {
827 | 		t.Errorf("time.Time not merged in properly: b.Birth(%v) != dataMap['Birth'](%v)", b.Birth, dataMap["Birth"])
828 | 	}
829 | }
830 | 
831 | type simpleNested struct {
832 | 	A int
833 | }
834 | 
835 | type structWithNestedPtrValueMap struct {
836 | 	NestedPtrValue map[string]*simpleNested
837 | }
838 | 
839 | func TestNestedPtrValueInMap(t *testing.T) {
840 | 	src := &structWithNestedPtrValueMap{
841 | 		NestedPtrValue: map[string]*simpleNested{
842 | 			"x": {
843 | 				A: 1,
844 | 			},
845 | 		},
846 | 	}
847 | 	dst := &structWithNestedPtrValueMap{
848 | 		NestedPtrValue: map[string]*simpleNested{
849 | 			"x": {},
850 | 		},
851 | 	}
852 | 	if err := mergo.Map(dst, src); err != nil {
853 | 		t.FailNow()
854 | 	}
855 | 	if dst.NestedPtrValue["x"].A == 0 {
856 | 		t.Errorf("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)
857 | 	}
858 | }
859 | 
860 | func loadFixture(path string) (m map[string]interface{}) {
861 | 	m = make(map[string]interface{})
862 | 	raw, _ := os.ReadFile(path)
863 | 	_ = json.Unmarshal(raw, &m)
864 | 	return
865 | }
866 | 
867 | type structWithMap struct {
868 | 	m map[string]structWithUnexportedProperty
869 | }
870 | 
871 | type structWithUnexportedProperty struct {
872 | 	s string
873 | }
874 | 
875 | func TestUnexportedProperty(t *testing.T) {
876 | 	a := structWithMap{map[string]structWithUnexportedProperty{
877 | 		"key": {"hello"},
878 | 	}}
879 | 	b := structWithMap{map[string]structWithUnexportedProperty{
880 | 		"key": {"hi"},
881 | 	}}
882 | 	defer func() {
883 | 		if r := recover(); r != nil {
884 | 			t.Errorf("Should not have panicked")
885 | 		}
886 | 	}()
887 | 	if err := mergo.Merge(&a, b); err != nil {
888 | 		t.Errorf("Error while merging %s", err)
889 | 	}
890 | }
891 | 
892 | type structWithBoolPointer struct {
893 | 	C *bool
894 | }
895 | 
896 | func TestBooleanPointer(t *testing.T) {
897 | 	bt, bf := true, false
898 | 	src := structWithBoolPointer{
899 | 		&bt,
900 | 	}
901 | 	dst := structWithBoolPointer{
902 | 		&bf,
903 | 	}
904 | 	if err := mergo.Merge(&dst, src); err != nil {
905 | 		t.FailNow()
906 | 	}
907 | 	if dst.C == src.C {
908 | 		t.Errorf("dst.C should be a different pointer than src.C")
909 | 	}
910 | 	if *dst.C != *src.C {
911 | 		t.Errorf("dst.C should be true")
912 | 	}
913 | }
914 | 
915 | func TestMergeMapWithInnerSliceOfDifferentType(t *testing.T) {
916 | 	testCases := []struct {
917 | 		name    string
918 | 		err     string
919 | 		options []func(*mergo.Config)
920 | 	}{
921 | 		{
922 | 			"With override and append slice",
923 | 			"cannot append two slices with different type",
924 | 			[]func(*mergo.Config){mergo.WithOverride, mergo.WithAppendSlice},
925 | 		},
926 | 		{
927 | 			"With override and type check",
928 | 			"cannot override two slices with different type",
929 | 			[]func(*mergo.Config){mergo.WithOverride, mergo.WithTypeCheck},
930 | 		},
931 | 	}
932 | 	for _, tc := range testCases {
933 | 		t.Run(tc.name, func(t *testing.T) {
934 | 			src := map[string]interface{}{
935 | 				"foo": []string{"a", "b"},
936 | 			}
937 | 			dst := map[string]interface{}{
938 | 				"foo": []int{1, 2},
939 | 			}
940 | 
941 | 			if err := mergo.Merge(&src, &dst, tc.options...); err == nil || !strings.Contains(err.Error(), tc.err) {
942 | 				t.Errorf("expected %q, got %q", tc.err, err)
943 | 			}
944 | 		})
945 | 	}
946 | }
947 | 
948 | func TestMergeDifferentSlicesIsNotSupported(t *testing.T) {
949 | 	src := []string{"a", "b"}
950 | 	dst := []int{1, 2}
951 | 
952 | 	if err := mergo.Merge(&src, &dst, mergo.WithOverride, mergo.WithAppendSlice); err != mergo.ErrDifferentArgumentsTypes {
953 | 		t.Errorf("expected %q, got %q", mergo.ErrNotSupported, err)
954 | 	}
955 | }
956 | 


--------------------------------------------------------------------------------
/pr211_2_test.go:
--------------------------------------------------------------------------------
 1 | package mergo
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 	"time"
 7 | )
 8 | 
 9 | type transformer struct {
10 | }
11 | 
12 | func (s *transformer) Transformer(t reflect.Type) func(dst, src reflect.Value) error {
13 | 	return nil
14 | }
15 | 
16 | func Test_deepMergeTransformerInvalidDestination(t *testing.T) {
17 | 	foo := time.Time{}
18 | 	src := reflect.ValueOf(foo)
19 | 	_ = deepMerge(reflect.Value{}, src, make(map[uintptr]*visit), 0, &Config{
20 | 		Transformers: &transformer{},
21 | 	})
22 | 	// this test is intentionally not asserting on anything, it's sole
23 | 	// purpose to verify deepMerge doesn't panic when a transformer is
24 | 	// passed and the destination is invalid.
25 | }
26 | 


--------------------------------------------------------------------------------
/pr211_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"reflect"
 5 | 	"testing"
 6 | 
 7 | 	"dario.cat/mergo"
 8 | )
 9 | 
10 | func TestMergeWithTransformerZeroValue(t *testing.T) {
11 | 	// This test specifically tests that a transformer can be used to
12 | 	// prevent overwriting a zero value (in this case a bool). This would fail prior to #211
13 | 	type fooWithBoolPtr struct {
14 | 		b *bool
15 | 	}
16 | 	var Bool = func(b bool) *bool { return &b }
17 | 	a := fooWithBoolPtr{b: Bool(false)}
18 | 	b := fooWithBoolPtr{b: Bool(true)}
19 | 
20 | 	if err := mergo.Merge(&a, &b, mergo.WithTransformers(&transformer{
21 | 		m: map[reflect.Type]func(dst, src reflect.Value) error{
22 | 			reflect.TypeOf(Bool(false)): func(dst, src reflect.Value) error {
23 | 				if dst.CanSet() && dst.IsNil() {
24 | 					dst.Set(src)
25 | 				}
26 | 				return nil
27 | 			},
28 | 		},
29 | 	})); err != nil {
30 | 		t.Error(err)
31 | 	}
32 | 
33 | 	if *a.b != false {
34 | 		t.Errorf("b not merged in properly: a.b(%v) != expected(%v)", a.b, false)
35 | 	}
36 | }
37 | 


--------------------------------------------------------------------------------
/pr80_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type mapInterface map[string]interface{}
10 | 
11 | func TestMergeMapsEmptyString(t *testing.T) {
12 | 	a := mapInterface{"s": ""}
13 | 	b := mapInterface{"s": "foo"}
14 | 	if err := mergo.Merge(&a, b); err != nil {
15 | 		t.Error(err)
16 | 	}
17 | 	if a["s"] != "foo" {
18 | 		t.Errorf("b not merged in properly: a.s.Value(%s) != expected(%s)", a["s"], "foo")
19 | 	}
20 | }
21 | 


--------------------------------------------------------------------------------
/pr81_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | func TestMapInterfaceWithMultipleLayer(t *testing.T) {
10 | 	m1 := map[string]interface{}{
11 | 		"k1": map[string]interface{}{
12 | 			"k1.1": "v1",
13 | 		},
14 | 	}
15 | 
16 | 	m2 := map[string]interface{}{
17 | 		"k1": map[string]interface{}{
18 | 			"k1.1": "v2",
19 | 			"k1.2": "v3",
20 | 		},
21 | 	}
22 | 
23 | 	if err := mergo.Map(&m1, m2, mergo.WithOverride); err != nil {
24 | 		t.Errorf("Error merging: %v", err)
25 | 	}
26 | 
27 | 	// Check overwrite of sub map works
28 | 	expected := "v2"
29 | 	actual := m1["k1"].(map[string]interface{})["k1.1"].(string)
30 | 	if actual != expected {
31 | 		t.Errorf("Expected %v but got %v",
32 | 			expected,
33 | 			actual)
34 | 	}
35 | 
36 | 	// Check new key is merged
37 | 	expected = "v3"
38 | 	actual = m1["k1"].(map[string]interface{})["k1.2"].(string)
39 | 	if actual != expected {
40 | 		t.Errorf("Expected %v but got %v",
41 | 			expected,
42 | 			actual)
43 | 	}
44 | }
45 | 


--------------------------------------------------------------------------------
/testdata/license.json:
--------------------------------------------------------------------------------
1 | {
2 |     "import": "./thing.json",
3 |     "fields": {
4 |         "site": "string",
5 |         "author": "updater"
6 |     }
7 | }


--------------------------------------------------------------------------------
/testdata/thing.json:
--------------------------------------------------------------------------------
1 | {
2 |     "fields": {
3 |         "id": "int",
4 |         "name": "string",
5 |         "parent": "ref datu:thing",
6 |         "status": "enum(draft, public, private)",
7 |         "author": "updater"
8 |     }
9 | }


--------------------------------------------------------------------------------
/v039_bugs_test.go:
--------------------------------------------------------------------------------
 1 | package mergo_test
 2 | 
 3 | import (
 4 | 	"testing"
 5 | 
 6 | 	"dario.cat/mergo"
 7 | )
 8 | 
 9 | type inner struct {
10 | 	A int
11 | }
12 | 
13 | type outer struct {
14 | 	inner
15 | 	B int
16 | }
17 | 
18 | func TestV039Issue139(t *testing.T) {
19 | 	dst := outer{
20 | 		inner: inner{A: 1},
21 | 		B:     2,
22 | 	}
23 | 	src := outer{
24 | 		inner: inner{A: 10},
25 | 		B:     20,
26 | 	}
27 | 	err := mergo.MergeWithOverwrite(&dst, src)
28 | 	if err != nil {
29 | 		panic(err.Error())
30 | 	}
31 | 	if dst.A == 1 {
32 | 		t.Errorf("expected %d, got %d", src.A, dst.A)
33 | 	}
34 | }
35 | 
36 | func TestV039Issue152(t *testing.T) {
37 | 	dst := map[string]interface{}{
38 | 		"properties": map[string]interface{}{
39 | 			"field1": map[string]interface{}{
40 | 				"type": "text",
41 | 			},
42 | 			"field2": "ohai",
43 | 		},
44 | 	}
45 | 	src := map[string]interface{}{
46 | 		"properties": map[string]interface{}{
47 | 			"field1": "wrong",
48 | 		},
49 | 	}
50 | 	if err := mergo.Map(&dst, src, mergo.WithOverride); err != nil {
51 | 		t.Error(err)
52 | 	}
53 | }
54 | 
55 | type issue146Foo struct {
56 | 	B map[string]issue146Bar
57 | 	A string
58 | }
59 | 
60 | type issue146Bar struct {
61 | 	C *string
62 | 	D *string
63 | }
64 | 
65 | func TestV039Issue146(t *testing.T) {
66 | 	var (
67 | 		s1 = "asd"
68 | 		s2 = "sdf"
69 | 	)
70 | 	dst := issue146Foo{
71 | 		A: "two",
72 | 		B: map[string]issue146Bar{
73 | 			"foo": {
74 | 				C: &s1,
75 | 			},
76 | 		},
77 | 	}
78 | 	src := issue146Foo{
79 | 		A: "one",
80 | 		B: map[string]issue146Bar{
81 | 			"foo": {
82 | 				D: &s2,
83 | 			},
84 | 		},
85 | 	}
86 | 	if err := mergo.Merge(&dst, src, mergo.WithOverride); err != nil {
87 | 		t.Error(err)
88 | 	}
89 | 	if dst.B["foo"].D == nil {
90 | 		t.Errorf("expected %v, got nil", &s2)
91 | 	}
92 | }
93 | 


--------------------------------------------------------------------------------