import merge-0.1.0 am: d6b6c53f3e am: b02ae3cc33 am: d233260174

Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/merge/+/2336343

Change-Id: I8000735d340042a60004d56a7092f22831170e8b
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..5e52de0
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,9 @@
+<!---
+SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+SPDX-License-Identifier: CC0-1.0
+-->
+
+# v0.1.0 (2020-09-01)
+
+Initial release providing the `Merge` trait and some merge strategies in the
+`bool`, `num`, `ord` and `vec` modules.
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..7ffd14e
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,372 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+# SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+# SPDX-License-Identifier: CC0-1.0
+[[package]]
+name = "ansi_term"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "atty"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "bitflags"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clap"
+version = "2.33.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "envy"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "heck"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "hermit-abi"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "lazy_static"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "libc"
+version = "0.2.76"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "merge"
+version = "0.1.0"
+dependencies = [
+ "envy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "merge_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "trybuild 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "merge_derive"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro-error 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "num-traits"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro-error"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro-error-attr 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "serde"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.114"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.57"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "strsim"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "structopt"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "structopt-derive 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "structopt-derive"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro-error 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "syn-mid"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "termcolor"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "textwrap"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "trybuild"
+version = "1.0.30"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)",
+ "serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)",
+ "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-width"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "unicode-xid"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vec_map"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "version_check"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "winapi-util"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[metadata]
+"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
+"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
+"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+"checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+"checksum envy 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f938a4abd5b75fe3737902dbc2e79ca142cc1526827a9e40b829a086758531a9"
+"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
+"checksum hermit-abi 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
+"checksum itoa 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
+"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
+"checksum libc 0.2.76 (registry+https://github.com/rust-lang/crates.io-index)" = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3"
+"checksum merge_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "209d075476da2e63b4b29e72a2ef627b840589588e71400a25e3565c4f849d07"
+"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
+"checksum proc-macro-error 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "fc175e9777c3116627248584e8f8b3e2987405cabe1c0adf7d1dd28f09dc7880"
+"checksum proc-macro-error-attr 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3cc9795ca17eb581285ec44936da7fc2335a3f34f2ddd13118b6f4d515435c50"
+"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
+"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+"checksum serde 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
+"checksum serde_derive 1.0.114 (registry+https://github.com/rust-lang/crates.io-index)" = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
+"checksum serde_json 1.0.57 (registry+https://github.com/rust-lang/crates.io-index)" = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c"
+"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
+"checksum structopt 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc388d94ffabf39b5ed5fadddc40147cb21e605f53db6f8f36a625d27489ac5"
+"checksum structopt-derive 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "5e2513111825077552a6751dfad9e11ce0fba07d7276a3943a037d7e93e64c5f"
+"checksum syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250"
+"checksum syn-mid 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7be3539f6c128a931cf19dcee741c1af532c7fd387baa739c03dd2e96479338a"
+"checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
+"checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a"
+"checksum trybuild 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "bbe777c4e2060f44d83892be1189f96200be8ed3d99569d5c2d5ee26e62c0ea9"
+"checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
+"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
+"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
+"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..0ced552
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,51 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+edition = "2018"
+name = "merge"
+version = "0.1.0"
+authors = ["Robin Krahl <[email protected]>"]
+exclude = [".builds/*"]
+description = "Merge multiple values into one"
+keywords = ["merge", "macros", "derive"]
+categories = ["rust-patterns"]
+license = "Apache-2.0 OR MIT"
+repository = "https://git.sr.ht/~ireas/merge-rs"
+[dependencies.merge_derive]
+version = "0.1.0"
+optional = true
+
+[dependencies.num-traits]
+version = "0.2.12"
+optional = true
+[dev-dependencies.envy]
+version = "0.4"
+
+[dev-dependencies.serde]
+version = "1.0"
+features = ["derive"]
+
+[dev-dependencies.structopt]
+version = "0.3"
+
+[dev-dependencies.toml]
+version = "0.5"
+
+[dev-dependencies.trybuild]
+version = "1.0"
+
+[features]
+default = ["derive", "num", "std"]
+derive = ["merge_derive"]
+num = ["num-traits"]
+std = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..aac6575
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,34 @@
+# SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+# SPDX-License-Identifier: CC0-1.0
+
+[package]
+name = "merge"
+version = "0.1.0"
+authors = ["Robin Krahl <[email protected]>"]
+edition = "2018"
+description = "Merge multiple values into one"
+repository = "https://git.sr.ht/~ireas/merge-rs"
+keywords = ["merge", "macros", "derive"]
+categories = ["rust-patterns"]
+license = "Apache-2.0 OR MIT"
+exclude = [".builds/*"]
+
+[dependencies]
+merge_derive = { path = "merge_derive", version = "0.1.0", optional = true }
+num-traits = { version = "0.2.12", optional = true }
+
+[dev-dependencies]
+envy = "0.4"
+toml = "0.5"
+trybuild = "1.0"
+structopt = "0.3"
+
+[dev-dependencies.serde]
+version = "1.0"
+features = ["derive"]
+
+[features]
+default = ["derive", "num", "std"]
+derive = ["merge_derive"]
+num = ["num-traits"]
+std = []
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..5431dc1
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSES/Apache-2.0.txt
\ No newline at end of file
diff --git a/LICENSES/Apache-2.0.txt b/LICENSES/Apache-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/LICENSES/Apache-2.0.txt
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..3204e4f
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+name: "merge"
+description: "Merge multiple values into one"
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://crates.io/crates/merge"
+  }
+  url {
+    type: ARCHIVE
+    value: "https://static.crates.io/crates/merge/merge-0.1.0.crate"
+  }
+  version: "0.1.0"
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2022
+    month: 11
+    day: 28
+  }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..4e1e118
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+include platform/prebuilts/rust:master:/OWNERS
[email protected]
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1bdb16a
--- /dev/null
+++ b/README.md
@@ -0,0 +1,98 @@
+<!---
+Copyright (C) 2020 Robin Krahl <[email protected]>
+SPDX-License-Identifier: CC0-1.0
+-->
+
+# merge-rs
+
+The `merge` crate provides the `Merge` trait that can be used to merge multiple
+values into one:
+
+```rust
+trait Merge {
+    fn merge(&mut self, other: Self);
+}
+```
+
+`Merge` is implemented for `Option` and can be derived for structs:
+
+<!-- should be kept in sync with examples/user.rs -->
+```rust
+use merge::Merge;
+
+#[derive(Merge)]
+struct User {
+    // Fields with the skip attribute are skipped by Merge
+    #[merge(skip)]
+    pub name: &'static str,
+
+    // The Merge implementation for Option replaces its value if it is None
+    pub location: Option<&'static str>,
+
+    // The strategy attribute is used to customize the merge behavior
+    #[merge(strategy = merge::vec::append)]
+    pub groups: Vec<&'static str>,
+}
+
+let defaults = User {
+    name: "",
+    location: Some("Internet"),
+    groups: vec!["rust"],
+};
+let mut ferris = User {
+    name: "Ferris",
+    location: None,
+    groups: vec!["mascot"],
+};
+ferris.merge(defaults);
+
+assert_eq!("Ferris", ferris.name);
+assert_eq!(Some("Internet"), ferris.location);
+assert_eq!(vec!["mascot", "rust"], ferris.groups);
+```
+
+A merge strategy is a function with the signature `fn merge<T>(left: &mut T,
+right: T)` that merges `right` into `left`.  The `merge` crate provides
+strategies for the most common types, but you can also define your own
+strategies.
+
+The trait can be used to merge configuration from different sources, for
+example environment variables, multiple configuration files and command-line
+arguments, see the `args.rs` example.
+
+## Features
+
+This crate has the following features:
+
+- `derive` (default):  Enables the derive macro for the `Merge` trait using the
+  `merge_derive` crate.
+- `num` (default): Enables the merge strategies in the `num` module that
+  require the `num_traits` crate.
+- `std` (default): Enables the merge strategies in the `vec` module that
+  require the standard library.  If this feature is not set, `merge` is a
+  `no_std` library.
+
+## Minimum Supported Rust Version
+
+This crate supports Rust 1.36.0 or later.
+
+## Contact
+
+For bug reports, patches, feature requests and other messages, please send a
+mail to [~ireas/[email protected]][] using the `[merge-rs]` prefix in
+the subject.
+
+## License
+
+This project is dual-licensed under the [Apache-2.0][] and [MIT][] licenses.
+The documentation and configuration files contained in this repository are
+licensed under the [Creative Commons Zero][CC0] license.  You can find a copy
+of the license texts in the `LICENSES` directory.
+
+`merge-rs` complies with [version 3.0 of the REUSE specification][reuse].
+
+[~ireas/[email protected]]: mailto:~ireas/[email protected]
+[Apache-2.0]: https://opensource.org/licenses/Apache-2.0
+[MIT]: https://opensource.org/licenses/MIT
+[CC0]: https://creativecommons.org/publicdomain/zero/1.0/
+[reuse]: https://reuse.software/practices/3.0/
diff --git a/examples/args.rs b/examples/args.rs
new file mode 100644
index 0000000..616a1d4
--- /dev/null
+++ b/examples/args.rs
@@ -0,0 +1,43 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: CC0-1.0
+
+use merge::Merge;
+use serde::Deserialize;
+use structopt::StructOpt;
+
+#[derive(Debug, Default, Deserialize, Merge, StructOpt)]
+#[serde(default)]
+struct Args {
+    #[structopt(short, long)]
+    #[merge(strategy = merge::bool::overwrite_false)]
+    debug: bool,
+
+    input: Option<String>,
+
+    output: Option<String>,
+}
+
+fn get_config() -> Option<Args> {
+    let path: &std::path::Path = "args.toml".as_ref();
+    if path.is_file() {
+        let s = std::fs::read_to_string(path).expect("Could not read configuration file");
+        Some(toml::from_str(&s).expect("Could not parse configuration"))
+    } else {
+        None
+    }
+}
+
+fn get_env() -> Args {
+    envy::prefixed("ARGS_")
+        .from_env()
+        .expect("Could not read environment variables")
+}
+
+fn main() {
+    let mut args = Args::from_args();
+    args.merge(get_env());
+    if let Some(config) = get_config() {
+        args.merge(config);
+    }
+    println!("{:?}", args);
+}
diff --git a/examples/user.rs b/examples/user.rs
new file mode 100644
index 0000000..60161d7
--- /dev/null
+++ b/examples/user.rs
@@ -0,0 +1,31 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: CC0-1.0
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct User {
+    #[merge(skip)]
+    pub name: &'static str,
+    pub location: Option<&'static str>,
+    #[merge(strategy = merge::vec::append)]
+    pub groups: Vec<&'static str>,
+}
+
+fn main() {
+    let defaults = User {
+        name: "",
+        location: Some("Internet"),
+        groups: vec!["rust"],
+    };
+    let mut ferris = User {
+        name: "Ferris",
+        location: None,
+        groups: vec!["mascot"],
+    };
+    ferris.merge(defaults);
+
+    assert_eq!("Ferris", ferris.name);
+    assert_eq!(Some("Internet"), ferris.location);
+    assert_eq!(vec!["mascot", "rust"], ferris.groups);
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..2deebca
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,223 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+//! Provides [`Merge`][], a trait for objects that can be merged.
+//!
+//! # Usage
+//!
+//! ```
+//! trait Merge {
+//!     fn merge(&mut self, other: Self);
+//! }
+//! ```
+//!
+//! The [`Merge`][] trait can be used to merge two objects of the same type into one.  The intended
+//! use case is merging configuration from different sources, for example environment variables,
+//! multiple configuration files and command-line arguments, see the [`args.rs`][] example.
+//!
+//! `Merge` is implemented for `Option` and can be derived for structs.  When deriving the `Merge`
+//! trait for a struct, you can provide custom merge strategies for the fields that don’t implement
+//! `Merge`.  A merge strategy is a function with the signature `fn merge<T>(left: &mut T, right:
+//! T)` that merges `right` into `left`.  The submodules of this crate provide strategies for the
+//! most common types, but you can also define your own strategies.
+//!
+//! ## Features
+//!
+//! This crate has the following features:
+//!
+//! - `derive` (default):  Enables the derive macro for the `Merge` trait using the `merge_derive`
+//!   crate.
+//! - `num` (default): Enables the merge strategies in the `num` module that require the
+//!   `num_traits` crate.
+//! - `std` (default): Enables the merge strategies in the `vec` module that require the standard
+//!   library.  If this feature is not set, `merge` is a `no_std` library.
+//!
+//! # Example
+//!
+//! ```
+//! use merge::Merge;
+//!
+//! #[derive(Merge)]
+//! struct User {
+//!     // Fields with the skip attribute are skipped by Merge
+//!     #[merge(skip)]
+//!     pub name: &'static str,
+//!
+//!     // The Merge implementation for Option replaces its value if it is None
+//!     pub location: Option<&'static str>,
+//!
+//!     // The strategy attribute is used to customize the merge behavior
+//!     #[merge(strategy = merge::vec::append)]
+//!     pub groups: Vec<&'static str>,
+//! }
+//!
+//! let defaults = User {
+//!     name: "",
+//!     location: Some("Internet"),
+//!     groups: vec!["rust"],
+//! };
+//! let mut ferris = User {
+//!     name: "Ferris",
+//!     location: None,
+//!     groups: vec!["mascot"],
+//! };
+//! ferris.merge(defaults);
+//!
+//! assert_eq!("Ferris", ferris.name);
+//! assert_eq!(Some("Internet"), ferris.location);
+//! assert_eq!(vec!["mascot", "rust"], ferris.groups);
+//! ```
+//!
+//! [`Merge`]: trait.Merge.html
+//! [`args.rs`]: https://git.sr.ht/~ireas/merge-rs/tree/master/examples/args.rs
+
+#![cfg_attr(not(feature = "std"), no_std)]
+
+#[cfg(feature = "derive")]
+pub use merge_derive::*;
+
+/// A trait for objects that can be merged.
+///
+/// # Deriving
+///
+/// `Merge` can be derived for structs if the `derive` feature is enabled.  The generated
+/// implementation calls the `merge` method for all fields, or the merge strategy function if set.
+/// You can use these field attributes to configure the generated implementation:
+/// - `skip`: Skip this field in the `merge` method.
+/// - `strategy = f`: Call `f(self.field, other.field)` instead of calling the `merge` function for
+///    this field.
+///
+/// # Examples
+///
+/// Using the `Merge` implementation for `Option`:
+///
+/// ```
+/// use merge::Merge as _;
+///
+/// let mut val = None;
+/// val.merge(Some(42));
+/// assert_eq!(Some(42), val);
+/// ```
+///
+/// Deriving `Merge` for a struct:
+///
+/// ```
+/// use merge::Merge;
+///
+/// #[derive(Debug, PartialEq, Merge)]
+/// struct S {
+///     option: Option<usize>,
+///
+///     #[merge(skip)]
+///     s: String,
+///
+///     #[merge(strategy = merge::bool::overwrite_false)]
+///     flag: bool,
+/// }
+///
+/// let mut val = S {
+///     option: None,
+///     s: "some ignored value".to_owned(),
+///     flag: false,
+/// };
+/// val.merge(S {
+///     option: Some(42),
+///     s: "some other ignored value".to_owned(),
+///     flag: true,
+/// });
+/// assert_eq!(S {
+///     option: Some(42),
+///     s: "some ignored value".to_owned(),
+///     flag: true,
+/// }, val);
+/// ```
+pub trait Merge {
+    /// Merge another object into this object.
+    fn merge(&mut self, other: Self);
+}
+
+impl<T> Merge for Option<T> {
+    fn merge(&mut self, mut other: Self) {
+        if !self.is_some() {
+            *self = other.take();
+        }
+    }
+}
+
+/// Merge strategies for boolean types.
+pub mod bool {
+    /// Overwrite left with right if the value of left is false.
+    pub fn overwrite_false(left: &mut bool, right: bool) {
+        if !*left {
+            *left = right;
+        }
+    }
+
+    /// Overwrite left with right if the value of left is true.
+    pub fn overwrite_true(left: &mut bool, right: bool) {
+        if *left {
+            *left = right;
+        }
+    }
+}
+
+/// Merge strategies for numeric types.
+///
+/// These strategies are only available if the `num` feature is enabled.
+#[cfg(feature = "num")]
+pub mod num {
+    /// Set left to the saturated some of left and right.
+    pub fn saturating_add<T: num_traits::SaturatingAdd>(left: &mut T, right: T) {
+        *left = left.saturating_add(&right);
+    }
+
+    /// Overwrite left with right if the value of left is zero.
+    pub fn overwrite_zero<T: num_traits::Zero>(left: &mut T, right: T) {
+        if left.is_zero() {
+            *left = right;
+        }
+    }
+}
+
+/// Merge strategies for types that form a total order.
+pub mod ord {
+    use core::cmp;
+
+    /// Set left to the maximum of left and right.
+    pub fn max<T: cmp::Ord>(left: &mut T, right: T) {
+        if cmp::Ord::cmp(left, &right) == cmp::Ordering::Less {
+            *left = right;
+        }
+    }
+
+    /// Set left to the minimum of left and right.
+    pub fn min<T: cmp::Ord>(left: &mut T, right: T) {
+        if cmp::Ord::cmp(left, &right) == cmp::Ordering::Greater {
+            *left = right;
+        }
+    }
+}
+
+/// Merge strategies for vectors.
+///
+/// These strategies are only available if the `std` feature is enabled.
+#[cfg(feature = "std")]
+pub mod vec {
+    /// Overwrite left with right if left is empty.
+    pub fn overwrite_empty<T>(left: &mut Vec<T>, mut right: Vec<T>) {
+        if left.is_empty() {
+            left.append(&mut right);
+        }
+    }
+
+    /// Append the contents of right to left.
+    pub fn append<T>(left: &mut Vec<T>, mut right: Vec<T>) {
+        left.append(&mut right);
+    }
+
+    /// Prepend the contents of right to left.
+    pub fn prepend<T>(left: &mut Vec<T>, mut right: Vec<T>) {
+        right.append(left);
+        *left = right;
+    }
+}
diff --git a/tests/compile.rs b/tests/compile.rs
new file mode 100644
index 0000000..aa1bda7
--- /dev/null
+++ b/tests/compile.rs
@@ -0,0 +1,9 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+#[test]
+#[ignore]
+fn test_compile_fail() {
+    let t = trybuild::TestCases::new();
+    t.compile_fail("tests/compile/*.rs");
+}
diff --git a/tests/compile/derive-enum.rs b/tests/compile/derive-enum.rs
new file mode 100644
index 0000000..6c4f9b2
--- /dev/null
+++ b/tests/compile/derive-enum.rs
@@ -0,0 +1,13 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+enum E {
+    V1,
+    V2,
+    V3,
+}
+
+fn main() {}
diff --git a/tests/compile/derive-enum.stderr b/tests/compile/derive-enum.stderr
new file mode 100644
index 0000000..4783549
--- /dev/null
+++ b/tests/compile/derive-enum.stderr
@@ -0,0 +1,7 @@
+error: merge::Merge can only be derived for structs
+ --> $DIR/derive-enum.rs:6:10
+  |
+6 | #[derive(Merge)]
+  |          ^^^^^
+  |
+  = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/compile/derive-invalid-attribute.rs b/tests/compile/derive-invalid-attribute.rs
new file mode 100644
index 0000000..cbc3e54
--- /dev/null
+++ b/tests/compile/derive-invalid-attribute.rs
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct S {
+    #[merge(ignore)]
+    field1: Option<u8>,
+}
+
+fn main() {}
diff --git a/tests/compile/derive-invalid-attribute.stderr b/tests/compile/derive-invalid-attribute.stderr
new file mode 100644
index 0000000..ee1b2c6
--- /dev/null
+++ b/tests/compile/derive-invalid-attribute.stderr
@@ -0,0 +1,5 @@
+error: Unexpected attribute: ignore
+ --> $DIR/derive-invalid-attribute.rs:8:13
+  |
+8 |     #[merge(ignore)]
+  |             ^^^^^^
diff --git a/tests/compile/derive-invalid-skip.rs b/tests/compile/derive-invalid-skip.rs
new file mode 100644
index 0000000..78ed93c
--- /dev/null
+++ b/tests/compile/derive-invalid-skip.rs
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct S {
+    #[merge(skip = true)]
+    field1: u8,
+}
+
+fn main() {}
diff --git a/tests/compile/derive-invalid-skip.stderr b/tests/compile/derive-invalid-skip.stderr
new file mode 100644
index 0000000..65679f9
--- /dev/null
+++ b/tests/compile/derive-invalid-skip.stderr
@@ -0,0 +1,5 @@
+error: expected `,`
+ --> $DIR/derive-invalid-skip.rs:8:18
+  |
+8 |     #[merge(skip = true)]
+  |                  ^
diff --git a/tests/compile/derive-invalid-strategy.rs b/tests/compile/derive-invalid-strategy.rs
new file mode 100644
index 0000000..46dc259
--- /dev/null
+++ b/tests/compile/derive-invalid-strategy.rs
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct S {
+    #[merge(strategy = my_custom_merge_strategy)]
+    field1: u8,
+}
+
+fn my_custom_merge_strategy(left: u8, right: u8) -> u8 {
+    left + right
+}
+
+fn main() {}
diff --git a/tests/compile/derive-invalid-strategy.stderr b/tests/compile/derive-invalid-strategy.stderr
new file mode 100644
index 0000000..ecdd085
--- /dev/null
+++ b/tests/compile/derive-invalid-strategy.stderr
@@ -0,0 +1,13 @@
+error[E0308]: mismatched types
+ --> $DIR/derive-invalid-strategy.rs:8:24
+  |
+8 |       #[merge(strategy = my_custom_merge_strategy)]
+  |  ________________________^
+9 | |     field1: u8,
+  | |__________^ expected `u8`, found `&mut u8`
+  |
+help: consider removing the borrow
+  |
+8 |     #[merge(strategy = my_custom_merge_strategy)]
+9 |     field1: u8,
+  |
diff --git a/tests/compile/derive-missing-strategy.rs b/tests/compile/derive-missing-strategy.rs
new file mode 100644
index 0000000..c9c1073
--- /dev/null
+++ b/tests/compile/derive-missing-strategy.rs
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct S {
+    #[merge(strategy = my_custom_merge_strategy)]
+    field1: u8,
+}
+
+fn main() {}
diff --git a/tests/compile/derive-missing-strategy.stderr b/tests/compile/derive-missing-strategy.stderr
new file mode 100644
index 0000000..9fb1a03
--- /dev/null
+++ b/tests/compile/derive-missing-strategy.stderr
@@ -0,0 +1,5 @@
+error[E0425]: cannot find function `my_custom_merge_strategy` in this scope
+ --> $DIR/derive-missing-strategy.rs:8:24
+  |
+8 |     #[merge(strategy = my_custom_merge_strategy)]
+  |                        ^^^^^^^^^^^^^^^^^^^^^^^^ not found in this scope
diff --git a/tests/compile/derive-no-strategy.rs b/tests/compile/derive-no-strategy.rs
new file mode 100644
index 0000000..10b95fa
--- /dev/null
+++ b/tests/compile/derive-no-strategy.rs
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct S {
+    #[merge(strategy)]
+    field1: u8,
+}
+
+fn main() {}
diff --git a/tests/compile/derive-no-strategy.stderr b/tests/compile/derive-no-strategy.stderr
new file mode 100644
index 0000000..9da8914
--- /dev/null
+++ b/tests/compile/derive-no-strategy.stderr
@@ -0,0 +1,5 @@
+error: expected `=`
+ --> $DIR/derive-no-strategy.rs:8:12
+  |
+8 |     #[merge(strategy)]
+  |            ^^^^^^^^^^
diff --git a/tests/compile/derive-u8.rs b/tests/compile/derive-u8.rs
new file mode 100644
index 0000000..25bfd9b
--- /dev/null
+++ b/tests/compile/derive-u8.rs
@@ -0,0 +1,12 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+#[derive(Merge)]
+struct S {
+    field1: Option<u8>,
+    field2: u8,
+}
+
+fn main() {}
diff --git a/tests/compile/derive-u8.stderr b/tests/compile/derive-u8.stderr
new file mode 100644
index 0000000..de3b5c2
--- /dev/null
+++ b/tests/compile/derive-u8.stderr
@@ -0,0 +1,7 @@
+error[E0277]: the trait bound `u8: merge::Merge` is not satisfied
+ --> $DIR/derive-u8.rs:9:5
+  |
+9 |     field2: u8,
+  |     ^^^^^^ the trait `merge::Merge` is not implemented for `u8`
+  |
+  = note: required by `merge::Merge::merge`
diff --git a/tests/derive.rs b/tests/derive.rs
new file mode 100644
index 0000000..0ac4163
--- /dev/null
+++ b/tests/derive.rs
@@ -0,0 +1,540 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+#![cfg(feature = "derive")]
+
+use merge::Merge;
+
+fn test<T: std::fmt::Debug + Merge + PartialEq>(expected: T, mut left: T, right: T) {
+    left.merge(right);
+    assert_eq!(expected, left);
+}
+
+#[test]
+fn test_one_option_field() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S {
+        field1: Option<usize>,
+    }
+
+    impl S {
+        pub fn new(field1: Option<usize>) -> S {
+            S { field1 }
+        }
+    }
+
+    test(S::new(Some(1)), S::new(Some(1)), S::new(Some(2)));
+    test(S::new(Some(1)), S::new(Some(1)), S::new(None));
+    test(S::new(Some(2)), S::new(None), S::new(Some(2)));
+    test(S::new(None), S::new(None), S::new(None));
+}
+
+#[test]
+fn test_two_option_fields() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S {
+        field1: Option<usize>,
+        field2: Option<usize>,
+    }
+
+    impl S {
+        pub fn new(field1: Option<usize>, field2: Option<usize>) -> S {
+            S { field1, field2 }
+        }
+    }
+
+    // left.field1 == Some(1)
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(1), Some(2)),
+        S::new(Some(1), None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == Some(1)
+    // right.field1 == None
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(Some(1), Some(2)),
+        S::new(Some(1), None),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(None, None),
+    );
+
+    // left.field1 == None
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(2), Some(2)),
+        S::new(None, None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), None),
+        S::new(None, None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == None
+    // right.field1 == None
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(None, Some(2)),
+        S::new(None, None),
+        S::new(None, Some(2)),
+    );
+    test(S::new(None, None), S::new(None, None), S::new(None, None));
+}
+
+#[test]
+fn test_skip_valid() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S {
+        field1: Option<usize>,
+        #[merge(skip)]
+        field2: Option<usize>,
+    }
+
+    impl S {
+        pub fn new(field1: Option<usize>, field2: Option<usize>) -> S {
+            S { field1, field2 }
+        }
+    }
+
+    // left.field1 == Some(1)
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == Some(1)
+    // right.field1 == None
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(None, None),
+    );
+
+    // left.field1 == None
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(2), None),
+        S::new(None, None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), None),
+        S::new(None, None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == None
+    // right.field1 == None
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(None, None),
+        S::new(None, None),
+        S::new(None, Some(2)),
+    );
+    test(S::new(None, None), S::new(None, None), S::new(None, None));
+}
+
+#[test]
+fn test_skip_invalid() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S {
+        field1: Option<usize>,
+        #[merge(skip)]
+        field2: usize,
+    }
+
+    impl S {
+        pub fn new(field1: Option<usize>, field2: usize) -> S {
+            S { field1, field2 }
+        }
+    }
+
+    // left.field1 == Some(1)
+    // right.field1 == Some(2)
+    test(S::new(Some(1), 1), S::new(Some(1), 1), S::new(Some(2), 2));
+    test(S::new(Some(1), 1), S::new(Some(1), 1), S::new(Some(2), 0));
+    test(S::new(Some(1), 0), S::new(Some(1), 0), S::new(Some(2), 2));
+    test(S::new(Some(1), 0), S::new(Some(1), 0), S::new(Some(2), 0));
+
+    // left.field1 == Some(1)
+    // right.field1 == None
+    test(S::new(Some(1), 1), S::new(Some(1), 1), S::new(None, 2));
+    test(S::new(Some(1), 1), S::new(Some(1), 1), S::new(None, 0));
+    test(S::new(Some(1), 0), S::new(Some(1), 0), S::new(None, 2));
+    test(S::new(Some(1), 0), S::new(Some(1), 0), S::new(None, 0));
+
+    // left.field1 == None
+    // right.field1 == Some(2)
+    test(S::new(Some(2), 1), S::new(None, 1), S::new(Some(2), 2));
+    test(S::new(Some(2), 1), S::new(None, 1), S::new(Some(2), 0));
+    test(S::new(Some(2), 0), S::new(None, 0), S::new(Some(2), 2));
+    test(S::new(Some(2), 0), S::new(None, 0), S::new(Some(2), 0));
+
+    // left.field1 == None
+    // right.field1 == None
+    test(S::new(None, 1), S::new(None, 1), S::new(None, 2));
+    test(S::new(None, 1), S::new(None, 1), S::new(None, 0));
+    test(S::new(None, 0), S::new(None, 0), S::new(None, 2));
+    test(S::new(None, 0), S::new(None, 0), S::new(None, 0));
+}
+
+#[test]
+fn test_strategy_usize_add() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S {
+        #[merge(strategy = add)]
+        field1: usize,
+    }
+
+    impl S {
+        pub fn new(field1: usize) -> S {
+            S { field1 }
+        }
+    }
+
+    fn add(left: &mut usize, right: usize) {
+        *left = *left + right;
+    }
+
+    test(S::new(0), S::new(0), S::new(0));
+    test(S::new(1), S::new(1), S::new(0));
+    test(S::new(1), S::new(0), S::new(1));
+    test(S::new(2), S::new(1), S::new(1));
+}
+
+#[test]
+fn test_strategy_vec_append() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S {
+        #[merge(strategy = append)]
+        field1: Vec<usize>,
+    }
+
+    impl S {
+        pub fn new(field1: Vec<usize>) -> S {
+            S { field1 }
+        }
+    }
+
+    fn append(left: &mut Vec<usize>, mut right: Vec<usize>) {
+        left.append(&mut right);
+    }
+
+    test(
+        S::new(vec![0, 1, 2, 3]),
+        S::new(vec![0, 1]),
+        S::new(vec![2, 3]),
+    );
+    test(
+        S::new(vec![0, 1, 2, 3]),
+        S::new(vec![0, 1, 2, 3]),
+        S::new(vec![]),
+    );
+    test(
+        S::new(vec![0, 1, 2, 3]),
+        S::new(vec![]),
+        S::new(vec![0, 1, 2, 3]),
+    );
+}
+
+#[test]
+fn test_unnamed_fields() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(Option<usize>, Option<usize>);
+
+    impl S {
+        pub fn new(field1: Option<usize>, field2: Option<usize>) -> S {
+            S(field1, field2)
+        }
+    }
+
+    // left.field1 == Some(1)
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(1), Some(2)),
+        S::new(Some(1), None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == Some(1)
+    // right.field1 == None
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(Some(1), Some(2)),
+        S::new(Some(1), None),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(None, None),
+    );
+
+    // left.field1 == None
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(2), Some(2)),
+        S::new(None, None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), None),
+        S::new(None, None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == None
+    // right.field1 == None
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(None, Some(2)),
+        S::new(None, None),
+        S::new(None, Some(2)),
+    );
+    test(S::new(None, None), S::new(None, None), S::new(None, None));
+}
+
+#[test]
+fn test_unnamed_fields_skip() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(Option<usize>, #[merge(skip)] Option<usize>);
+
+    impl S {
+        pub fn new(field1: Option<usize>, field2: Option<usize>) -> S {
+            S(field1, field2)
+        }
+    }
+
+    // left.field1 == Some(1)
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == Some(1)
+    // right.field1 == None
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), Some(1)),
+        S::new(Some(1), Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(Some(1), None),
+        S::new(Some(1), None),
+        S::new(None, None),
+    );
+
+    // left.field1 == None
+    // right.field1 == Some(2)
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), Some(1)),
+        S::new(None, Some(1)),
+        S::new(Some(2), None),
+    );
+    test(
+        S::new(Some(2), None),
+        S::new(None, None),
+        S::new(Some(2), Some(2)),
+    );
+    test(
+        S::new(Some(2), None),
+        S::new(None, None),
+        S::new(Some(2), None),
+    );
+
+    // left.field1 == None
+    // right.field1 == None
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, Some(2)),
+    );
+    test(
+        S::new(None, Some(1)),
+        S::new(None, Some(1)),
+        S::new(None, None),
+    );
+    test(
+        S::new(None, None),
+        S::new(None, None),
+        S::new(None, Some(2)),
+    );
+    test(S::new(None, None), S::new(None, None), S::new(None, None));
+}
diff --git a/tests/impls.rs b/tests/impls.rs
new file mode 100644
index 0000000..d8378e5
--- /dev/null
+++ b/tests/impls.rs
@@ -0,0 +1,16 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+use merge::Merge;
+
+fn test<T: std::fmt::Debug + Merge + PartialEq>(expected: T, mut left: T, right: T) {
+    left.merge(right);
+    assert_eq!(expected, left);
+}
+
+#[test]
+fn test_option() {
+    test(Some(1), Some(1), Some(2));
+    test(Some(2), None, Some(2));
+    test(None::<usize>, None, None);
+}
diff --git a/tests/strategies.rs b/tests/strategies.rs
new file mode 100644
index 0000000..3a35ea2
--- /dev/null
+++ b/tests/strategies.rs
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: 2020 Robin Krahl <[email protected]>
+// SPDX-License-Identifier: Apache-2.0 or MIT
+
+#![cfg(feature = "derive")]
+
+use merge::Merge;
+
+fn test<T: std::fmt::Debug + Merge + PartialEq>(expected: T, mut left: T, right: T) {
+    left.merge(right);
+    assert_eq!(expected, left);
+}
+
+#[test]
+fn test_bool_overwrite_false() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::bool::overwrite_false)] bool);
+
+    test(S(false), S(false), S(false));
+    test(S(true), S(false), S(true));
+    test(S(true), S(true), S(false));
+    test(S(true), S(true), S(true));
+}
+
+#[test]
+fn test_bool_overwrite_true() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::bool::overwrite_true)] bool);
+
+    test(S(false), S(false), S(false));
+    test(S(false), S(false), S(true));
+    test(S(false), S(true), S(false));
+    test(S(true), S(true), S(true));
+}
+
+#[cfg(feature = "num")]
+#[test]
+fn test_num_saturating_add() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::num::saturating_add)] u8);
+
+    test(S(0), S(0), S(0));
+    test(S(1), S(0), S(1));
+    test(S(255), S(255), S(10));
+    test(S(40), S(30), S(10));
+}
+
+#[cfg(feature = "num")]
+#[test]
+fn test_num_overwrite_zero() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::num::overwrite_zero)] u8);
+
+    test(S(0), S(0), S(0));
+    test(S(1), S(0), S(1));
+    test(S(255), S(255), S(10));
+}
+
+#[test]
+fn test_ord_max() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::ord::max)] u8);
+
+    test(S(2), S(1), S(2));
+    test(S(2), S(2), S(1));
+    test(S(2), S(2), S(2));
+    test(S(2), S(2), S(0));
+    test(S(2), S(0), S(2));
+    test(S(33), S(33), S(11));
+}
+
+#[test]
+fn test_ord_min() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::ord::min)] u8);
+
+    test(S(1), S(1), S(2));
+    test(S(1), S(2), S(1));
+    test(S(2), S(2), S(2));
+    test(S(0), S(2), S(0));
+    test(S(0), S(0), S(2));
+    test(S(11), S(33), S(11));
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn test_vec_overwrite_empty() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::vec::overwrite_empty)] Vec<u8>);
+
+    test(S(vec![]), S(vec![]), S(vec![]));
+    test(S(vec![1]), S(vec![]), S(vec![1]));
+    test(S(vec![0]), S(vec![0]), S(vec![1]));
+    test(S(vec![255]), S(vec![255]), S(vec![10]));
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn test_vec_append() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::vec::append)] Vec<u8>);
+
+    test(S(vec![]), S(vec![]), S(vec![]));
+    test(S(vec![1]), S(vec![]), S(vec![1]));
+    test(S(vec![0, 1]), S(vec![0]), S(vec![1]));
+    test(S(vec![255, 10]), S(vec![255]), S(vec![10]));
+    test(S(vec![0, 1, 2, 3, 4]), S(vec![0, 1, 2]), S(vec![3, 4]));
+    test(S(vec![3, 4, 0, 1, 2]), S(vec![3, 4]), S(vec![0, 1, 2]));
+}
+
+#[cfg(feature = "std")]
+#[test]
+fn test_vec_prepend() {
+    #[derive(Debug, Merge, PartialEq)]
+    struct S(#[merge(strategy = merge::vec::prepend)] Vec<u8>);
+
+    test(S(vec![]), S(vec![]), S(vec![]));
+    test(S(vec![1]), S(vec![]), S(vec![1]));
+    test(S(vec![1, 0]), S(vec![0]), S(vec![1]));
+    test(S(vec![10, 255]), S(vec![255]), S(vec![10]));
+    test(S(vec![3, 4, 0, 1, 2]), S(vec![0, 1, 2]), S(vec![3, 4]));
+    test(S(vec![0, 1, 2, 3, 4]), S(vec![3, 4]), S(vec![0, 1, 2]));
+}