diff options
author | Frederick Mayle <fmayle@google.com> | 2022-12-09 18:27:28 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2022-12-09 18:27:28 +0000 |
commit | 79173e2c0d662eceacd1feed84ef303e2471b1dd (patch) | |
tree | d96293100261c58fcaa7023504f7717ba8604480 | |
parent | e59a73e955cab538261ae9b9bf64fd02a7cc2c2d (diff) | |
parent | d23326017413043f14310af13109b326873737dc (diff) | |
download | merge-79173e2c0d662eceacd1feed84ef303e2471b1dd.tar.gz |
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 <android-build-automerger-merge-worker@system.gserviceaccount.com>
31 files changed, 1908 insertions, 0 deletions
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 <robin.krahl@ireas.org> +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 <robin.krahl@ireas.org> +# 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 <robin.krahl@ireas.org>"] +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 <robin.krahl@ireas.org> +# SPDX-License-Identifier: CC0-1.0 + +[package] +name = "merge" +version = "0.1.0" +authors = ["Robin Krahl <robin.krahl@ireas.org>"] +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 = [] @@ -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 @@ -0,0 +1,2 @@ +include platform/prebuilts/rust:master:/OWNERS +fmayle@google.com 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 <robin.krahl@ireas.org> +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/public-inbox@lists.sr.ht][] 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/public-inbox@lists.sr.ht]: mailto:~ireas/public-inbox@lists.sr.ht +[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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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 <robin.krahl@ireas.org> +// 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])); +} |